微信支付宝合并为一个二维码支付的实现

背景说明

我们是一个位于三线城市的购物中心,虽然已经开业多年,但是受益于良好的招商和日常运营,客流量在同城同类型项目中一直比较稳定。在节假日出场车流高峰期的时候,由于人工收费的效率问题,会导致车辆积压在收费口等待缴费。我曾在停车场出口掐表测算过,人工收费平均需要20-30秒才能出一辆车。这个问题严重影响到了驾车客户的体验,因此我们在官微上提供了停车场在线缴费功能,让提前线上缴费的用户到达停车场出口的时候无须等待直接离开,将一辆车通过出口闸机的时间降低到了平均2-3秒,通行效率大幅提升。

在线缴费功能前期是以H5实现的,17年移植到了小程序版。上线首月在线提前缴费的用户比例就达到了20%左右,经过一段时间运行,这个比例稳定在70%以上,彻底解决了停车场出口拥堵的问题。
2017年小程序版首次提交的git日志
小程序版在线缴费的UI

由于官微上只能支持微信支付(好像是废话),为了支持部分支付宝缴费用户,也为了补全缴费途径,公司计划在停车场电梯厅内部署几台缴费机。缴费机采用了落地式触摸屏,由于采用的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页面参照了官方支付风格。

微信支付H5页面

这样一套流程跑下来,基本把微信和支付宝公用一个二维码支付的问题解决了,后面如果计划支持QQ支付、百度钱包、京东支付之类也可以依葫芦画瓢。

猜你喜欢

转载自www.cnblogs.com/minibear2000/p/11645987.html