Django整合微信h5支付

    终于在踩完了无数坑之后,发现这个流程必须记录下来。在这之前,本人不仅尝试过网上各种各样的帖子,也参考了微信支付的官方文档,都最后都是发现:要么难以理解,要么甚至按照微信支付的官方文档也会遇到问题。

    虽然如果完全参考官方的文档会被误导,但是其中一部分还是可以参考的。

    首先来看要准备的环境:

    1.认证的微信公众号,并且开通了微信支付

    2.开通商户平台账号

    3.带域名的服务器

    以上三点是对接微信h5支付的前提条件,接下来我们可以参考微信支付的官方文档:由于是h5页面调用的微信支付,所以这里点击链接后选择JSAPI支付。在统一下单的列表中,微信给出了需要请求的API以及参数。由于我参考的其他的技术贴自己请求微信的API一直没成功,所以最后我还是直接调用了微信官方对支付封装好的代码,代码可以在github上下载。下载好之后,需要分别将weixin文件夹中的config.py、lib.py和pay.py脚本文件拷贝到我们的工程目录下。拷贝完成后对这些脚本文件稍作配置:

    config.py脚本中需要配置的都是些微信公众号的信息,像APPID、APPSECRET等,其中都有注释。登录到微信公众平台,在基本配置中可以看到一下信息:

    

    这里就有APPID、APPSECRET和TOKEN,服务器地址要配置成服务器上的一个接口(服务器带上域名),在提交配置的时候微信会对我们配置的这个接口发送一个请求,接口需要接收微信传来的参数以及返回正确的响应才能配置成功。服务器上的接口如下:

class WXRest(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, request):
        signature = request.GET.get('signature')
        timestamp = request.GET.get('timestamp')
        nonce = request.GET.get('nonce')
        echostr = request.GET.get('echostr')
        wechat_instance = WechatBasic(conf=wxConf)
        if not wechat_instance.check_signature(signature=signature, timestamp=timestamp, nonce=nonce):
            return HttpResponseBadRequest('Verify Failed')
        else:
            return HttpResponse(echostr, content_type="application/json")

    wxConf的配置如下:

wxConf = WechatConf(
    token='xxxxxx',
    appid=SOCIAL_AUTH_WEIXIN_APPID,
    appsecret=SOCIAL_AUTH_WEIXIN_SECRET,
    encrypt_mode='normal',
    encoding_aes_key='xxxxxx'
)

    其中token需要填写之前公众号开发信息中的令牌(Token),appid就是公众号中的开发者ID,appsecret为开发者密码,encoding_aes_key为服务器配置项中的服务器加解密密钥。

    我这里使用的drf的API View,注意其中的

authentication_classes = []
permission_classes = []

    这两句是取消该接口的token认证和登录认证,否则如果接口上有任何的权限认证,微信的请求都是不成功的。

    config.py脚本中剩下的商户ID MCHID和商户支付密钥KEY则需要登录到微信商户平台中获取。

    做好这些配置之后,就可以正式开始View的编写了:

from utils.wechatUtils.pay import JsApi_pub, UnifiedOrder_pub

class WxPayConfig(APIView):
    def post(self, request):
        money = request.data[u"number"]
        admin_user = AdminUser.objects.filter(username=request.user.username)[0]
        master = Master.objects.filter(admin_user=admin_user)[0]
        socialAccounts = SocialAccounts.objects.filter(admin_user=admin_user)[0]
        openid = socialAccounts.openid
        money = int(float(money)*100)
        out_trade_no = genOrder(master.phone)
        jsApi = JsApi_pub()
        unifiedOrder = UnifiedOrder_pub()
        unifiedOrder.setParameter("openid", openid)
        unifiedOrder.setParameter("body", "储值卡充值")
        unifiedOrder.setParameter("out_trade_no", out_trade_no)
        unifiedOrder.setParameter("total_fee", str(money))
        unifiedOrder.setParameter("notify_url", NOTIFY_URL)
        unifiedOrder.setParameter("trade_type", "JSAPI")
        prepay_id = unifiedOrder.getPrepayId()
        jsApi.setPrepayId(prepay_id)
        jsApiParameters = jsApi.getParameters()
        conn = redis.StrictRedis()
        conn.set("out_trade_no_" + out_trade_no, json.dumps({"type": "储值卡充值", "admin_user": admin_user.id},
                                                            ensure_ascii=False), 60 * 10)
        return HttpResponse(json.dumps(json.loads(jsApiParameters)), content_type="application/json")

      这里同样是使用drf的APIView,number参数是从前端提交的支付金额,从微信提供的pay.py中导入JsApi_pub和UnifiedOrder_pub,然后设置所需的参数。其中参数openid就是用户微信的openid,body是商品名称,out_trade_no为我们自己服务器生成的订单号,total_fee就是付款金额,notify_url为微信支付成功后回调的我们服务器的URL,trade_type参数为JSAPI表示支付类型为h5页面支付。填写完这些参数后,调用UnifiedOrder_pub类中的getPrepayId函数获得prepay_id,并进行设置。最后将调用jsApi.getParameters()函数返回的结果返回到前端。

      生成参数out_trade_no的函数如下:

def genOrder(phone="176******"):
    id_number = str(phone) + str(time.time())
    resId = str(uuid.uuid3(uuid.NAMESPACE_URL, id_number))
    resId = u"".join(re.findall("\d+", resId))
    return resId

    上面的代码片段是根据用户的手机生成对应的唯一下单ID。当支付成功后,微信会将支付结果以post请求的发送提交到NOTIFY_URL,对应的View接受参数如下:

class PayResult(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, request):
        self.post(request)

    def post(self, request):
        if request.body != "":
            xmlDict = xmlParse(request.body)
            print json.dumps(xmlDict, ensure_ascii=False)
            if xmlDict.has_key(u'return_code') and xmlDict[u'return_code'] == u'SUCCESS':
                total_fee = xmlDict[u'total_fee']
                out_trade_no = xmlDict[u'out_trade_no']
                try:
                    with transaction.atomic():
                        conn = redis.StrictRedis()
                        data = conn.get("out_trade_no_" + out_trade_no)
                        if data is not None:
                            jsonData = json.loads(data)
                            admin_user_id = jsonData[u"admin_user"]
                            if jsonData[u"type"] == u"储值卡充值":
                                admin_user = AdminUser.objects.filter(id=admin_user_id)[0]
                                assets = Assets.objects.filter(admin_user=admin_user)
                                if len(assets) == 0:
                                    assets = Assets()
                                    assets.assets = float(total_fee) / 100
                                    assets.admin_user = admin_user
                                else:
                                    assets = assets[0]
                                    assets.assets = float(assets.assets) + float(total_fee) / 100
                                assets.save()
                                assetsDetail = AssetsDetail()
                                assetsDetail.balance = float(total_fee) / 100
                                assetsDetail.type = 1
                                assetsDetail.note = jsonData[u"type"]
                                assetsDetail.admin_user = admin_user
                                assetsDetail.save()
                except BaseException as e:
                    print e.message
            return_data = {"return_code": "SUCCESS", "return_msg": "OK"}
            return HttpResponse(trans_dict_to_xml(return_data), content_type="application/xml")

    当支付成功时,微信提交的参数中会包含return_code并且值为SUCCESS,这里需要注意的是,微信提交的参数类型为xml,且我们返回给微信的数据类型也为xml,若处理成功,需要返回:

<xml>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <return_msg><![CDATA[OK]]></return_msg>
</xml>

    注意这里的格式并不是常见的xml格式,而是微信它自己的<![CDATA[,这也是让人很麻烦的地方。将json数据封装成微信的xml格式数据函数如下:

def trans_dict_to_xml(data_dict):
    data_xml = []
    for k in sorted(data_dict.keys()):
        v = data_dict.get(k)
        if k == 'detail' and not v.startswith('<![CDATA['):
            v = '<![CDATA[{}]]>'.format(v)
        data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
    return '<xml>{}</xml>'.format(''.join(data_xml)).encode('utf-8')

    最后,在前端处理我们后台返回的结果并调起微信支付:

sendAjax({"number":number},"/wx-pay-config",function (data) {
                wx.config({
                    debug:false,
                    appId:data["appId"],
                    timestamp:data["timeStamp"],
                    nonceStr:data["nonceStr"],
                    signature:data["paySign"],
                    package:data["package"]
                });
                wx.ready(function () {
                    wx.chooseWXPay({
                        timestamp: data["timeStamp"], // 支付签名时间戳
                        nonceStr: data["nonceStr"], // 支付签名随机串,不长于32 位
                        package: data["package"], // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
                        signType: "MD5", // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
                        paySign: data["paySign"], // 支付签名
                        success: function (res) {
                            //支付成功
                            mui.alert("支付成功");
                        },
                        cancel: function (res) {
                            //支付取消
                            mui.alert("支付已取消");
                        }
                    });
                });
                wx.error(function (res) {
                    console.log("error:" + res);
                });
            });

sendAjax是我自己封装的一个提交POST形式的ajax函数,大家使用普通的ajax发送post请求就可以了。这里需要注意的是,最后我调用了一个wx.error的函数,我原以为是在支付出错时调用的,但经过测试后发现:即使支付成功了,这个error函数依然会被执行,很容易被误导,以为支付没成功。这里值得一提的是,在微信官方文档中,前端调起支付的代码如下:

function onBridgeReady(){
   WeixinJSBridge.invoke(
      'getBrandWCPayRequest', {
         "appId":"wx2421b1c4370ec43b",     //公众号名称,由商户传入     
         "timeStamp":"1395712654",         //时间戳,自1970年以来的秒数     
         "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串     
         "package":"prepay_id=u802345jgfjsdfgsdg888",     
         "signType":"MD5",         //微信签名方式:     
         "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
      },
      function(res){
      if(res.err_msg == "get_brand_wcpay_request:ok" ){
      // 使用以上方式判断前端返回,微信团队郑重提示:
            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
      } 
   }); 
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}

经过测试后发现,这样写并不能调起支付,是一个完全没反应的状态。这是微信文档很坑的地方,需要大家特别注意。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jaris_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值