微信公众号使用uniappH5、python对接微信支付V3-JSAPI的支付功能

微信公众号网页用uniapp,后台用的python,近期对接微信支付-apiv3版-jsapi支付,特此整理记录方便日后查找使用

apiv3升级后,请求体使用不用xml使用json,每次请求需要在header中添加签名,而签名需要用微信支付的商户证书私钥进行RSA加密

使用公众号进行对接流程如下

  1. 先保存公众号开发所需要的access_token与jssdk调用需要的jsapi_ticket
  2. 引入微信js-sdk
  3. 添加公众号域名白名单
  4. 请求后台,返回注入js-sdk所需要的参数
  5. 使用参数对js-sdk进行注入
  6. 使用公众号文档中的网页授权流程第一步先获取到授权所需code
  7. 使用code请求后台,后台通过网页授权流程第二步获取用户的openid后,一并返回支付所需要的参数
  8. 使用支付参数进行sdk的支付调用
  9. 后台通过回调接口来接收并保存支付状态

1、自行保存公众号access_token与jsapi_ticket

公众号access_token获取文档
公众号网页开发jsapi_ticket获取文档

uniapp-支付页面先引入注入微信网页开发所用的JS-SDK

微信公众号网页开发文档地址

// 支付页面代码
// 在页面onload中引入微信sdk,在APP.vue引入好像有点问题,不过为了保险起见,在App.vue页面再引入了一次
onLoad(e) {
	var script =document.createElement('script');
	script.src = "/static/js/jweixin-1.6.0.js";
	script.type = 'text/javascript';
	document.body.appendChild(script)
}
// App.vue页面
onShow: function() {
	console.log('App Show')
	// 引入微信sdk
	const script = document.createElement('script');
	script.src = "https://res.wx.qq.com/open/js/jweixin-1.6.0.js";
	script.type = 'text/javascript';
	document.body.appendChild(script)
}

2、在微信公众号 头像>功能设置页中进行配置域名白名单,把业务域名、JS接口安全域名、网页授权域名
全部添加。

公众号设置超链接

微信公众号使用uniappH5、python对接微信支付V3-JSAPI的支付功能_第1张图片

3、请求后台,返回注入js所需要的参数
注入时遇到的问题
3.1、签名出错,查看参数,查看加密方式,查看url参数(出错较多)
3.2、jsapi_list为空列表[]也不可以,提示fail,添加一个权限就可以了

注入js-sdk微信文档

//uniapp支付页关键代码
//注入js有一个当前页面的url,所以这里把当前的url保存传到后台,之前后台写死会出现不一致的问题,保险起见,这里由前端把url传入

var currentUrl = window.location.href;
var token = uni.getStorageSync('token')
	var data = {
		token: token,
		appid: 'xxxx',
		url: currentUrl
	}

# python端关键代码-js注入参数生成

def jszhuru(appid, url, debug):
    """jsapi注入"""
	#生成随机数
    noncestr = str(uuid.uuid4()).replace('-', '')
	# 这里请求数据库,拿到jsapi_ticket
    # jsapi_ticket=xxxxxxxxxx
    timestamp = int(time.time())

    sing_str = "jsapi_ticket={}&noncestr={}×tamp={}&url={}".format(jsapi_ticket, noncestr, timestamp, url)
    sha1_hash = hashlib.sha1()
    sha1_hash.update(sing_str.encode('utf-8'))
    sha1_sign = sha1_hash.hexdigest()

    result = {
        'debug': debug,
        "appid": appid,
        'noncestr': noncestr,
        "timestamp": timestamp,
        "signature": sha1_sign,
        "url": url,
        "jsapi_list": ['chooseImage']
    }

    return result

4、h5端注入js-sdk

// uniapp支付页关键代码

wx.config({
	debug: e.data.data.result.debug, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
	appId: String(e.data.data.result.appid), // 必填,公众号的唯一标识
	timestamp: String(e.data.data.result.timestamp), // 必填,生成签名的时间戳
	nonceStr: String(e.data.data.result.noncestr), // 必填,生成签名的随机串
	signature: String(e.data.data.result.signature), // 必填,签名
	jsApiList: e.data.data.result.jsapi_list // 必填,需要使用的JS接口列表
});

wx.ready(function() {
	that.zhuru_status = true
});

wx.error(function(res) {
	that.zhuru_status = false
	uni.showToast({
		title:'微信支付加载失败',
		icon:'none'
	})
});

5、使用前端传来的code获取openid,之后调用下单接口,获取prepay_id,然后返回下单所需要的参数,前端调用支付功能

code获取openid微信文档

# 获取openid关键代码
url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=xxx&secret=xxx&code=' + code + '&grant_type=authorization_code'
resp = requests.get(url)
resp_dict = json.loads(resp.text)
print(resp.text)
openid = resp_dict.get('openid')
# 后台请求jsapi下单接口关键代码
def xiadan(out_trade_no,description, amount_total, openid):
    """jsapi下单"""

    # 订单过期时间
    now = datetime.datetime.now()
    # 计算1小时后的时间
    future_time = now + relativedelta(hours=1)
    # 将时间格式化为RFC 3339标准格式
    time_expire = future_time.strftime('%Y-%m-%dT%H:%M:%S.%f') + 'Z'

    appid = 'xxxx'

    body_dict_list = [
        {'key': 'appid', 'doc': '应用id', 'value': appid},
        {'key': 'mchid', 'doc': '商户id', 'value': MCHID},
        {'key': 'description', 'doc': '商品描述', 'value': description},
        {'key': 'out_trade_no', 'doc': '内部订单号', 'value': out_trade_no},
        {'key': 'time_expire', 'doc': '订单过期时间', 'value': time_expire},
        {'key': 'notify_url', 'doc': '回调url', 'value': NOTIFY_URL},
        {'key': 'amount', 'doc': '金额', 'value': {'total': amount_total}},
        {'key': 'payer', 'doc': 'openid', 'value': {'openid': openid}},

    ]

    # 2、字典排序
    body_sort_list = sorted(body_dict_list, key=lambda x: x['key'])

    # 生成请求体
    body_dict = {}
    for item in body_sort_list:
        key = item.get('key')
        value = item.get('value')
        body_dict[key] = value

    # 拼接证书的路径
    current_file_path = os.path.abspath(__file__)
    current_directory = os.path.dirname(current_file_path)
    current_cwd = current_directory.replace('\\', '/')
    current_cwd_end_index = current_cwd.rfind('/')
    dir_path = current_cwd[:current_cwd_end_index]
    private_path = os.path.join(dir_path, 'cert', 'pkcs1_private.pem')
    cert_path = os.path.join(dir_path, 'cert', 'apiclient_cert.pem')
    print('私钥路径', private_path)
    print('证书路径', cert_path)

    # 8、发起请求
    result = wxfunc.wxpay_http(method='POST', path='/v3/pay/transactions/jsapi', body=body_dict,
                               private_key_pkcs1_path=private_path, cert_path=cert_path,
                               )

    return json.loads(result)


	
# 后台生成调用支付参数部分-生成签名的关键代码

nonce_str = str(uuid.uuid4()).replace('-', '')
time_stamp = int(time.time())
appid = 'wxxxxxxx'
package = 'prepay_id=' + prepay_id

# 生成签名
sign_str = '{}\n{}\n{}\n{}\n'.format(appid,
                                    time_stamp,
                                    nonce_str,
                                    package
                                    )
sign = sha256_with_rsa(sign_str, private_path)

amount_total = order_info.amount_total
amount_str = str(Decimal(str(amount_total)) / 100)

body_dict_list = [
   {'key': 'appId', 'doc': '应用id', 'value': str(appid)},
   {'key': 'timeStamp', 'doc': '时间戳', 'value': str(time_stamp)},
   {'key': 'nonceStr', 'doc': '随机数', 'value': str(nonce_str)},
   {'key': 'package', 'doc': '下单id', 'value': str(package)},
   {'key': 'signType', 'doc': '签名类型', 'value': 'RSA'},
   {'key': 'paySign', 'doc': '签名值', 'value': sign},
   {'key': 'description', 'doc': '商品描述', 'value': order_info.description},
   {'key': 'amount', 'doc': '金额', 'value': amount_str},
   {'key': 'order_id', 'doc': '订单号', 'value': order_id},
]

"""...返回调用支付所需要的参数"""

6、前端获取调用所需要的参数,发起支付

微信调起jsapi支付方式文档

// js调起支付

WeixinJSBridge.invoke('getBrandWCPayRequest', {
	"appId": that.zhifu_dict.appId, //公众号ID,由商户传入     
	"timeStamp": that.zhifu_dict.timeStamp, //时间戳,自1970年以来的秒数     
	"nonceStr": that.zhifu_dict.nonceStr, //随机串     
	"package": that.zhifu_dict.package,
	"signType": that.zhifu_dict.signType, //微信签名方式:     
	"paySign": that.zhifu_dict.paySign
},
function(res) {
	if (res.err_msg == "get_brand_wcpay_request:ok") {
		uni.showToast({
			title:'支付成功',
			icon:'success'
		})
		// 使用以上方式判断前端返回,微信团队郑重提示:
		//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
	}
});

你可能感兴趣的:(微信支付,python,uniapp,微信)