一文搞懂微信公众号中的网页授权支付

花了一些时间读完了微信公众号号支付流程,并实现了微信支付,在开发中遇见了不少坑,现在一一给大家讲解并说明解决方法。

准备步骤:

1、准备申请好的公众号,公众号中设置与开发-----基本配置中能够获取到公众号的id和secret:

        APPID:123456789123456

        AppSecret:123456789qwerty

2、 IP白名单设置:通过开发者ID及密码调用获取access_token接口时,需要设置访问来源IP为白名单。而access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。

3、服务器配置:

        1.URL:具体的一个服务器上请求路径,这里主要是用于验证消息的确来自微信服务器

        2.token需要自己配置,微信上的token需要和服务端的token一致,主要用于验证。

        这个地方配置了以后主要是用来做接收微信公众号中触发的各种事件:同名的该url需要get和post两种方式去实现,get请求用于做验证,post请求用于做微信回调事件触发。

举例:

@router.get("/checkOpenToken")
async def checkOpenToken(signature, timestamp, nonce, echostr):
    try:
        if sign_sha1(signature, timestamp, nonce):
            return int(echostr)
        else:
            logger.error("加密字符串 不等于 微信返回字符串,验证失败!!!")
            return "验证失败!"
    except Exception as error:
        return f"微信服务器配置验证出现异常:{error}"


@router.post("/checkOpenToken", summary="回复微信消息")
async def subscribed(request: Request, signature, timestamp, nonce, openid):
    if sign_sha1(signature, timestamp, nonce):
        try:
            rec_msg = await request.body()
            root = ET.fromstring(rec_msg)
            msg_type = root.find("MsgType").text
            adminid = root.find("ToUserName").text
            openid = root.find("FromUserName").text
            create_time = root.find("CreateTime").text
            event = root.find("Event").text
            eventKey = root.find("EventKey").text
            ####TODO   业务代码
            return JSONResponse(status_code=status.HTTP_200_OK, content="success"
                                )
        except Exception as error:
            logger.error(f"微信回复信息报错:{error}")
            return JSONResponse(status_code=status.HTTP_200_OK, content=error
                                )

def sign_sha1(signature, timestamp, nonce):
    temp = [settings.token, timestamp, nonce]
    temp.sort()
    hashcode = hashlib.sha1("".join(temp).encode('utf-8')).hexdigest()
    logger.info(f"加密:{hashcode},微信返回:{signature}")
    if hashcode == signature:
        return True

这里是公众号消息接收功能完成了。

接下来就是微信网页授权开发了:

       想要在公众号中嵌入自己的web页面需要在设置与开发----功能设置----网页授权域名中添加你的域名,你需要下载一个txt文件并放在根目录下,微信在你设置域名时去判断该文件是否存在,并且文件中内容是否一致。如此来确保页面正确。

之后你就可以访问该域名下的所有静态文件了。并在这些页面中你能够通过页面获取到网页授权access_token,这个网页授权access_token和普通的access_token是没有任何关联的。

网页授权token获取

最后一步我们该如何唤起微信支付呢?

首先登录【微信支付商户平台 (opens new window)—>产品中心—>开发配置】,设置后一般5分钟内生效。

这里需要选择JSAPI支付:按照规则填写,这里的域名必须是备案的。

一文搞懂微信公众号中的网页授权支付_第1张图片

这里我们可以在商户平台拿到我们需要的一些配置:

        1.商户id:123456789qwert

        2.商户证书序列号:由微信商户平台支持

        3.APIv3秘钥:需要申请商户API证书(只有超级管理员才能操作),API证书是有CA颁发,在APIv3中,调用微信支付所有接口需要

        4.私钥证书:将生成的证书放在项目中

开发步骤:

        1.将前端收到的回调:code携带,调用

        "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + settings.appid + "&secret=" + settings.appsecret + "&code=" + code + "&grant_type=authorization_code"

这个接口将会返回网页授权token,获取到网页授权token后,

        2.前端调用下订单接口:会返回给前端需要的prepay_id,paySign,noncestr,timestamp

这里为什么要将noncestr,timestamp一起返回呢,因为是要保持前端真正唤起微信支付的校验一致。支付完成后,就等着支付回调了。

        3.支付回调接口就是在前端发起真正支付请求时配置的,拿到支付回调我们需要解密回调接口收到的信息,返回所有传入的参数,进行我们入库的操作。

预支付:

@router.post("/order")
async def createOrder(wxpayRequest: WxpayRequest):
    # 调用接口创建订单
    try:
        out_trade_no = worker.get_id()
        description = goods_exist.goods_name
        print("description:" + str(description))
        amount = wxpayRequest.payer_total
        print("amount:" + str(amount))
        payer = {'openid': wxpayRequest.openid}
        print("payer:" + str(payer))
        code, message = wxpay.pay(
            description=description,
            out_trade_no=str(out_trade_no),
            amount={'total': amount},
            pay_type=WeChatPayType.JSAPI,
            payer=payer
        )
        print("code:" + str(code))
        result = json.loads(message)
        print("message:" + str(result))
        if code in range(200, 300):
            prepay_id = result.get('prepay_id')
            logger.info("获取预支付id" + str(prepay_id))
            timestamp = str(int(time.time()))
            noncestr = str(uuid.uuid4()).replace('-', '')
            package = 'prepay_id=' + prepay_id
            paySign = wxpay.sign([settings.appid, timestamp, noncestr, package])
            print("打印预支付签名:" + str(paySign))
            logger.info(f"创建一条order记录成功")
            return JSONResponse(status_code=status.HTTP_200_OK, content={
                "prepay_id": prepay_id,
                "paySign": paySign,
                "noncestr": noncestr,
                "timestamp": timestamp
            }
                                )
    except Exception:
        raise HTTPException(status_code=400, detail="下单失败,请稍后重试")

支付回调:

@router.post("/notify")
async def notify(request: Request):
    headers = {
        'Wechatpay-Signature': request.headers.get('wechatpay-signature'),
        'Wechatpay-Timestamp': request.headers.get('wechatpay-timestamp'),
        'Wechatpay-Nonce': request.headers.get('wechatpay-nonce'),
        'Wechatpay-Serial': request.headers.get('wechatpay-serial')
    }
    result = wxpay.callback(headers=headers, body=await request.body())
    print("打印回调结果:" + str(result))
    if result and result.get('event_type') == 'TRANSACTION.SUCCESS':
        resp = result.get('resource')
        out_trade_no = resp.get('out_trade_no')
        transaction_id = resp.get('transaction_id')
        success_time = resp.get('success_time')
        payer = resp.get('payer')
        print("打印用户:" + str(payer))
        openid = payer.get("openid")
        print("打印openid:" + openid)
        amount = resp.get('amount').get('total')
        # TODO: 根据返回参数进行必要的业务处理,处理完后返回200或204
        successTime = datetime.now()
        return {'code': 'SUCCESS', 'message': '成功'}
    else:
        return {'code': 'FAILED', 'message': '失败'}

至此整个业务流程完毕,并且能够实现微信支付。如果想要完整的工具和代码流程方法,请加入我们的公众号,我们的公众号是可以实现PDF总结,图片总结,网页总结等功能的哦,是一款十分好用的公众号,并且会定期推送对于开发有帮助的文章,我们一直关注于最新的AI前沿技术。

一文搞懂微信公众号中的网页授权支付_第2张图片

你可能感兴趣的:(微信)