Day18 - [丰收款] 提供信用卡付款以及取得PayToken流程

在我们花了不少时间终於完整的完成ATM付款并成功架设Heroku网站後取回PayToken,更新付款资讯。有了这些基础後,我们在实作信用卡的部份基本上都可以用走到飞的状态来进行。

有兴趣可以参考从Day12开始到Day17的文章,Day17基本上已将完整的流程与架构做了完成度很高的铺垫。

刷好刷满!提供信用卡给客户吧

https://ithelp.ithome.com.tw/upload/images/20211003/20130354CIbQWMFzjO.png

https://ithelp.ithome.com.tw/upload/images/20211003/20130354sGZMrneDG7.png

以上为永丰规格书中提供的信用卡刷卡内容,和ATM的流程稍有不同就是在於PayToken的取得方式,下面来说明。

流程拆解

按照先前的ATM付款流程,信用卡的流程差在没有中间的BackendURL的等待呼叫过程,整个流程从我们的电商平台到取得信用卡刷卡位置转址,再转回来,流程是衔接在一起的。

  1. [View] 提供模拟建立新的信用卡付款订单的网页 (/order/create_card_order)
    • 乱数产生订单总金额
    • 呼叫Model相对的create_new_card_order
  2. [Model] 建立新信用卡付款的订单 (APIService: CreateOrder)
    • 建立CreditCardMessage类别物件 (继承至ApiMessage)
    • 执行set_shop_data()设定订单相关资料
      • 提供ReturnURL网址,待刷卡完成後导回网页,并处理由永丰回传的PayToken
    • 执行create_new_order()方法进行各种所需流程之资料准备(加密、运算签章等),最後叫用API
    • ResponseCard类别物件取回回传值,最重要的是CardPayURL。 (继承至ResponseMessage)
    • 更新Payment Model ORM物件 → 自动更新资料库
  3. 导入或提供信用卡URL,让消费者进行刷卡
    • 接下来为人工流程,於页面中输入信用卡资讯,在此我们使用永丰提供的测试信用卡资料刷入。
    • 刷完卡後,永丰会依照我们先前传入的ReturnURL网址将页面导回
  4. [View] 於ReturnURL页面(/order/card_return)取回PayToken值
  5. [Model] 包装PayToken进行API呼叫 (APIService: OrderPayQuery)
    • 建立QueryByPaytokenMessage类别物件 (继承至ApiMessage)
    • 执行set_paytoken_json()设定PayToken所需相关资料
    • 执行send_query()方法进行各种所需流程之资料准备(加密、运算签章等),最後叫用API
    • ResponsePayToken类别物件取回回传值 (继承至ResponseMessage)
    • 取出PayToken问回的订单,将付款状态写回Payment Model ORM物件 → 自动更新资料库

物件导向类别规划

和先前流程一样,因为我们已经建立了两套共用的父类别(ApiMessageResponseMessage),分别可从中继承扩充新的Message呼叫方法以及扩充取回的response内容。

所以我们就新增一个CreditCardMessage(继承ApiMessage),以及ResponseCard(继承ResponseMessage),因此更新一下我们的物件架构图。

https://ithelp.ithome.com.tw/upload/images/20211003/20130354AdLcomfxd3.png

建立信用卡付款类别CreditCardMessage

class CreditCardMessage(ApiMessage):
    return_url = "https://kummyshop.herokuapp.com/order/card_return"

    def __init__(self):
        super().__init__("OrderCreate")
        self.shop_data = None

    def set_shop_data(self, will_paid=True, amount=100, auto_billing="Y"):
        self.shop_data = CreditCardMessage.gen_default_shop_data(self.shop_no, will_paid, amount)
        self.shop_data["PayType"] = "C"
        self.shop_data["CardParam"]["AutoBilling"] = auto_billing
        self.shop_data["ReturnURL"] = CreditCardMessage.return_url
        self.shop_data["BackendURL"] = SinopacUtil.backend_url
        print("- shop_data: {}".format(self.shop_data))
        super()._set_plain_msg_to_proc_msg_sign(self.shop_data)

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

    @staticmethod
    def gen_default_shop_data(shop_no, will_paid=True, amount=100):
        tmp_data = ApiMessage._gen_temp_shop_data()
        tmp_data["ShopNo"] = shop_no
        tmp_data["OrderNo"] = SinopacUtil.gen_order_no(will_paid)
        tmp_data["Amount"] = amount * 100
        tmp_data["PrdtName"] = "信用卡订单"

        return tmp_data

程序说明

这边和ATM基本上很像,差异仅在ATMParam与CardParam的部份,至於PrdtName则仅作个注记用途,没有太实际的功能性。主要还是初始化後,设定shop_data後,就可以进行Send API的流程。

另外就是我们需要准备好ReturnURL给永丰API,届时刷卡完成後会再导回我们的网页。

实际流程当然相当复杂,只是我们先前将Code经过整理後,目前只要有新的作法就可以叠加在已架构好的程序架构上进行叫用,无需每次都要重覆复制贴上一堆八、九成都一样的的程序码,这就是实践程序码的DRY(Don't repeat yourself)精神。

至於其他程序段,请参阅Day17的部份,在此就不再贴过来占版面。

建立Response回传类别CreditCardMessage

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

        self.orderno = self.dec_resp_json["OrderNo"]
        self.amount = self.dec_resp_json["Amount"]
        self.tsno = self.dec_resp_json["TSNo"]
        self.status = self.dec_resp_json["Status"]
        self.desc = self.dec_resp_json["Description"]
        self.card_pay_url = self.dec_resp_json["CardParam"]["CardPayURL"]

这里就是针对CardPayURL进行处理,简单来说,就是我们既然要提供顾客刷卡服务,且透过永丰API,那我们要作的其实是建立订单後请永丰给我们一个信卡用刷卡的金流网址,我们收到後就可以将客户导入後刷卡,待刷卡完成後会再导回我们的页面 (我们先前提供的ReturnURL要用在这里)。而导回我们的页面,另一个要点是要处理PayToken

直接来试刷了!

待我们将程序码布署上Heroku後,就可以马上来试用。

建立新订单,取回信用卡刷卡位置,点选导入刷卡网址:
https://ithelp.ithome.com.tw/upload/images/20211003/20130354KzBMa0Zp49.png

进行刷卡,虽然永丰开发规格书上有提供测试用的刷卡卡号,但毕竟不是完全的公开资料,不确定被有心人士拿去乱使用会不会造成相关单位困扰,所以还是马赛克一下。
https://ithelp.ithome.com.tw/upload/images/20211003/201303541CnfiiLBLf.png

网页导回来了,但发生了错误!

在刷卡成功後,网页回来是回来了,但却发生了以下的错误。

500 Internal Server Error

原先因为Heroku上面我使用另一组production_settings设定档,上面DEBUG是False,导致看不出原因。

以下是永丰的文件说提到,无论是ReturnURL或BackendURL都是以下面方式将PayToken传入:
https://ithelp.ithome.com.tw/upload/images/20211003/20130354ZnUycKvu5f.png

真相只有一个

後来使用了F12大法,透过Network方式查看Request的内容,发现如下内容。
https://ithelp.ithome.com.tw/upload/images/20211003/20130354hbDe5IsWEw.png

於是解读了一下,原先在ATM的BackendURL中,我是使用POST中将JSON内容从Raw Data方法取回,但一样的方式会出现错误。

另外我再将DEBUG Mode打开後,确认他是透过POST Form的方式回传PayToken,而非JSON。

https://ithelp.ithome.com.tw/upload/images/20211003/20130354NxgTDXOAyz.png

以此可证实,透过ReturnURL回传的PayToken值,是需要使用POST Form去抓取的。
(这部份永丰的开发规格书可能要注明一下)

上方的PayToken的值会和我等会儿下面页面截图上的值不同,因为页面是後来全部修正完後重新上版跑过,所以两者值不一样是可想而知。

重新修改[View] card_return

def card_return(request):
    pay_token_dic = {"PayToken": request.POST.get("PayToken"), "ShopNo": request.POST.get("ShopNo")}
    print(" --- PayToken: {}, ShopNo: {}".format(pay_token_dic["PayToken"], pay_token_dic["ShopNo"]))
    create_new_paytoken(pay_token_dic["PayToken"])
    update_order_by_paytoken(pay_token_dic)
    return HttpResponse("PayToken: {}<br />ShopNo: {}".format(pay_token_dic["PayToken"], pay_token_dic["ShopNo"]))
程序说明

首先,我们先直接从POST中取资料,而非先前的request.body。取得後,一样是需要将PayToken拿去重新经过一连串的加密包装後取回付款状态,并且更新。先前都写好包装好的程序码,这时候就可以1分钟内实作完成。

再看一下页面,果然可以了!

当然,实际的电商页面不会是这样把PayToken印在画面上,一切都是为了测试API的流程与验证而已。真正要做的是将由PayToken问回来的付款结果,更新在画面上,告知顾客他刷卡成功了。这部份就交给各位来实作了。

https://ithelp.ithome.com.tw/upload/images/20211003/20130354MoTs5mTloO.png

一样的我们回到pgAdmin中看一下资料的更新状态,也都符合我们的预期。!https://ithelp.ithome.com.tw/upload/images/20211003/20130354mX2coEcose.png

这样总算是将丰收款的两大功能:ATM虚拟帐户付款信用卡付款服务完成了!

/images/emoticon/emoticon07.gif


<<:  #17-不用套件让网站Logo动起来~(SVG SMIL)

>>:  Day19 网页的页首header

Day 22 - [API] 使用 PHP 执行 Python 脚本

嗨! 昨天终於结束了语料库模型建置的部分,再来就要建立 API 了。这个系统中我采用了一个比较特别的...

我该问甚麽篇之找工作小Tips

终於来到最後一天最後一篇啦!!!! 真的好感动QQ 成功完赛了 第三次挑战铁人赛成功~ 每一年都是难...

【第 28 个第一次】 Prototype 好重要,Marvelapp 让点子瞬间视觉飞起来 !

Day 28 - 你们服务项目有 Prototype,通常会位於流程的哪一阶段呢? 在目前短暂的在外...

Last Night in Soho线上

Last Night in Soho线上 《Soho区惊魂夜》是由埃德加·赖特执导,安雅·泰勒-乔伊...

人脸辨识-day15 应用层面--1

人脸辨识的技术可使用两种方式搭建云端上或边缘装置上,那要如何选择将系统搭在哪个架构上,就要先了解系统...