背景說明
我們是一個位於三線城市的購物中心,雖然已經開業多年,但是受益於良好的招商和日常運營,客流量在同城同類型項目中一直比較穩定。在節假日出場車流高峰期的時候,由於人工收費的效率問題,會導致車輛積壓在收費口等待繳費。我曾在停車場出口掐表測算過,人工收費平均需要20-30秒才能出一輛車。這個問題嚴重影響到了駕車客戶的體驗,因此我們在官微上提供了停車場在線繳費功能,讓提前線上繳費的用戶到達停車場出口的時候無須等待直接離開,將一輛車通過出口閘機的時間降低到了平均2-3秒,通行效率大幅提升。
在線繳費功能前期是以H5實現的,17年移植到了小程序版。上線首月在線提前繳費的用戶比例就達到了20%左右,經過一段時間運行,這個比例穩定在70%以上,徹底解決了停車場出口擁堵的問題。
由於官微上只能支持微信支付(好像是廢話),為了支持部分支付寶繳費用戶,也為了補全繳費途徑,公司計划在停車場電梯廳內部署幾台繳費機。繳費機采用了落地式觸摸屏,由於采用的c/s架構,前端就是一個簡單的UI呈現,因此對性能幾乎沒有要求。
一碼支付
在設計繳費機系統的過程中,涉及到需要同時支持微信支付和支付寶支付,基本的業務邏輯就是用戶提交車牌號到后台,后台查詢到費用后用微信支付寶sdk生成二維碼支付參數,后台再把參數反饋給繳費機,等待用戶通過微信或支付寶客戶端掃描二維碼付款。
在最初的第一個版本中,為了同時支持微信和支付寶,我們在繳費機主界面上放置了兩個二維碼,提示用戶用對應的app掃描支付,這兩個二維碼分別使用微信的Native支付和支付寶的當面付產品。很顯然這個體驗並不會很好,於是准備通過一個二維碼同時支持微信和支付寶。
調用微信python sdk獲取Native支付url
wxpay = WXPay(app_id=WXAPPID, mch_id=WXMCHID, key=WXKEY, cert_pem_path=None, key_pem_path=None, timeout=6000, use_sandbox=False)
wxresp = wxpay.unifiedorder(dict(body='三盛國際廣場-停車繳費-%s' % plateno,
out_trade_no='%s' % (orderNo,),
product_id=plateno,
total_fee=payable,
time_start=datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
time_expire=(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime("%Y%m%d%H%M%S"),
notify_url='https://apiserver/wxnotify',
trade_type='NATIVE'))
if wxresp['return_code'].upper() == 'SUCCESS' and wxresp['result_code'].upper() == 'SUCCESS' and wxresp['appid'] == WXAPPID:
wxurl = wxresp['code_url']
wxurl是類似下面這樣的格式:
weixin://wxpay/bizpayurl?sign=XX&appid=XX&mch_id=XX&product_id=XX&time_stamp=XX&nonce_str=XX
調用支付寶python sdk獲取當面付url
alipay_client_config = AlipayClientConfig()
alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
alipay_client_config.app_id = ALI_APPID
alipay_client_config.app_private_key = ALI_PRI_KEY
alipay_client_config.alipay_public_key = ALI_PUB_KEY
aliclient = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)
alimodel = AlipayTradePrecreateModel()
alimodel.out_trade_no = orderNo
alimodel.total_amount = payable / 100
alimodel.subject = "三盛國際廣場-停車費-%s" % plateno
alimodel.qr_code_timeout_express = "5m"
alirequest = AlipayTradePrecreateRequest(biz_model=alimodel)
udf_params = dict()
udf_params[P_NOTIFY_URL] = "https://apiserver/alinotify"
alirequest.udf_params = udf_params
aliresponse = aliclient.execute(alirequest)
if aliresponse:
response = AlipayTradePrecreateResponse()
response.parse_response_content(aliresponse)
if response.is_success():
aliurl = response.qr_code
aliurl是類似下面這樣的格式:
https://qr.alipay.com/bavh4wjlxf12tper3a
在第一個版本中,繳費機顯示的二維碼分別是微信和支付寶服務器返回給我們的參數編碼而成的,既然需要一碼支付,顯然不能直接拿來用,畢竟這兩個巨頭都不支持自家APP掃對方的二維碼。
於是我們想到的是通過一個我們自己生成的二維碼作為橋梁,當獲取到微信和支付寶返回的支付參數后,先臨時保存下來,然后制作一個帶參數的H5頁面傳送給繳費機生成二維碼給用戶掃碼,當app掃碼解析出頁面地址訪問的時候,服務器根據瀏覽器agent信息跳轉對應的支付url給app即可。
if 'MicroMessenger' in agent:
#生成微信Native支付url
return redirect(wxurl)
elif 'Alipay' in agent:
#生成支付寶當面付支付url
return redirect(aliurl)
else:
return '請打開微信或支付寶掃描付款二維碼!'
這個邏輯看起來好像沒問題,很完美的解決了用戶體驗上的問題。實際上里面還有一個坑,就是支付寶當面付這樣操作沒任何問題,而微信支付不允許這樣操作,微信會判斷支付二維碼是通過攝像頭掃描直接獲取還是瀏覽器跳轉過來的,當判斷非瀏覽器直接掃碼會拒絕支付,微信支付這樣做的原因可能避免安全問題。
既然微信的Native支付不能直接用,我只能曲線救國了,因為我們前期已經有過一個H5繳停車費的版本,因此這次直接拿來重新改造了一下,當判斷為微信掃碼的時候,調用微信的JSAPI支付,在H5頁面內完成支付。
if 'MicroMessenger' in agent:
#生成微信h5支付url
return redirect(wxh5url)
elif 'Alipay' in agent:
#生成支付寶當面付支付url
return redirect(aliurl)
else:
return '請打開微信或支付寶掃描付款二維碼!'
較之支付寶的當面付來說,需要多做一個H5支付頁面,為了讓體驗和原生支付一致,我們的H5頁面參照了官方支付風格。
這樣一套流程跑下來,基本把微信和支付寶公用一個二維碼支付的問題解決了,后面如果計划支持QQ支付、百度錢包、京東支付之類也可以依葫蘆畫瓢。