微信jsapi支付调起 用的prepay_id的MD5签名

记录一次微信jsapi支付调起用的签名

主要场景是在微信内打开链接进行支付。使用md5签名,rsa签名没成功,真tm服了。
首先已经成功创建了预支付订单,也就是得到了prepay_id了,如果还没有到这一步本文的内容暂时还无关哈。

构建签名字符串

此处需要注意一定要在最后加商户平台设置的密钥key,加在构建的字符串后面,不然会显示签名验证失败,去tm的!!!代码如下

import hashlib
import six

def _cal_pay_sign(self, app_id, timestamp, nonceStr, prepay_id):
	data = {
   
   
	    'appId': app_id, # 公众号appid
	    'timeStamp': timestamp, # 创建预订单时的时间戳(10位)
	    'nonceStr': nonceStr, # 创建预订单时随机字符
	    'signType': 'MD5', # 签名方式
	    'package': f'prepay_id={
     
     prepay_id}', # prepa
<think> 我们正在处理微信支付接口中获取prepay_id的问题。根据用户提供的引用内容,我们可以总结出以下关键点: 1. 微信支付接口调用成功后,会返回XML格式的数据,其中包含prepay_id(引用[1])。 2. 在生成支付参数时,需要用到prepay_id来生成签名(引用[4])。 3. 微信支付回调的处理需要注意,回调参数是通过流传递的(引用[3])。 用户需要的是获取prepay_id的方法和相关示例,因此我们将重点放在如何调用接口获取prepay_id。 步骤: 1. 构造请求参数:包括appid、mch_id、nonce_str、sign(签名)、body、out_trade_no、total_fee、spbill_create_ip、notify_url、trade_type等。 2. 生成签名(sign):按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(key1=value1&key2=value2...)拼接成字符串,然后拼接商户密钥,进行MD5或HMAC-SHA256加密,并转换成大写。 3. 将参数转换为XML格式,发送到微信支付统一下单接口(URL:https://api.mch.weixin.qq.com/pay/unifiedorder)。 4. 解析返回的XML数据,从中获取prepay_id。 注意: - 必须设置回调地址(notify_url),用于接收支付结果通知(引用[2])。 - 返回的XML数据需要用正确的方式解析。 下面是一个Java示例代码(由于引用中提到了回调参数通过流传递,因此在回调处理中要注意从流中读取参数): 获取prepay_id的示例(统一下单): ```java import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class WeixinPay { // 统一下单接口URL private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 生成随机字符串 private static String generateNonceStr() { return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); } // 生成签名 public static String createSign(SortedMap<String, String> params, String key) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) { String k = entry.getKey(); String v = entry.getValue(); if (v != null && !v.isEmpty() && !k.equals("sign")) { sb.append(k).append("=").append(v).append("&"); } } sb.append("key=").append(key); String signStr = sb.toString(); // 使用MD5加密,并转换为大写 String sign = MD5Util.MD5(signStr).toUpperCase(); return sign; } // 获取prepay_id public static String getPrepayId() throws Exception { // 构造请求参数 SortedMap<String, String> params = new TreeMap<>(); params.put("appid", "你的appid"); params.put("mch_id", "你的商户号"); params.put("nonce_str", generateNonceStr()); params.put("body", "商品描述"); params.put("out_trade_no", "商户订单号"); params.put("total_fee", "订单总金额(单位分)"); params.put("spbill_create_ip", "客户端IP"); params.put("notify_url", "支付结果回调地址"); params.put("trade_type", "JSAPI"); // 小程序支付JSAPI,APP支付填APP,H5支付填MWEB // 生成签名 String sign = createSign(params, "你的商户密钥"); params.put("sign", sign); // 将参数转换为XML String xml = mapToXml(params); // 发送HTTP请求 String response = HttpUtil.post(UNIFIED_ORDER_URL, xml); // 解析返回的XML Map<String, String> result = parseXml(response); String returnCode = result.get("return_code"); if ("SUCCESS".equals(returnCode)) { String resultCode = result.get("result_code"); if ("SUCCESS".equals(resultCode)) { return result.get("prepay_id"); } else { throw new Exception("获取prepay_id失败: " + result.get("err_code_des")); } } else { throw new Exception("请求失败: " + result.get("return_msg")); } } // 将Map转换为XML字符串 private static String mapToXml(Map<String, String> map) { StringBuilder xml = new StringBuilder(); xml.append("<xml>"); for (Map.Entry<String, String> entry : map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); xml.append("<").append(key).append(">").append(value).append("</").append(key).append(">"); } xml.append("</xml>"); return xml.toString(); } // 解析XML字符串为Map private static Map<String, String> parseXml(String xml) throws Exception { Map<String, String> map = new HashMap<>(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8")); Document doc = builder.parse(is); Element root = doc.getDocumentElement(); NodeList nodeList = root.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { if (nodeList.item(i) instanceof Element) { Element element = (Element) nodeList.item(i); map.put(element.getNodeName(), element.getTextContent()); } } return map; } } ``` 注意事项: 1. 确保商户密钥正确,因为签名错误会导致请求失败。 2. 订单号(out_trade_no)需要保证唯一性。 3. 回调地址(notify_url)必须是外网可访问的,不能带参数(如需要参数,可以放在路径中),且不能有端口号(微信要求为80或443端口)。 4. 在回调处理方法中,微信服务器会将回调数据通过流(InputStream)的形式传递,因此不能通过传统的request.getParameter()获取,而是要从输入流中读取。 回调处理示例(Java): ```java public void notifyWeiXinPay(HttpServletRequest request, HttpServletResponse response) throws IOException { // 读取回调数据流 BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream())); String line; StringBuilder sb = new StringBuilder(); while ((line = br.readLine()) != null) { sb.append(line); } String xml = sb.toString(); // 解析XML Map<String, String> map = parseXml(xml); // 验证签名(略,需要按照同样的规则生成签名并与map中的sign对比) // 处理业务逻辑... // 返回给微信服务器的响应(必须为success的XML,否则微信会重复通知) String responseXml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; response.getWriter().write(responseXml); } ``` 总结: 获取prepay_id的关键是调用统一下单接口,构造正确的参数并生成签名。在回调处理中需要注意从输入流中读取参数。 相关问题: 1. 微信支付统一下单接口返回错误码该如何处理? 2. 如何验证微信支付回调的签名? 3. 微信支付回调处理时需要注意哪些安全问题? </think> ### 微信支付接口获取 `prepay_id` 的方法及示例 #### 核心流程 1. **调用统一下单接口** 向微信支付服务器发送请求(`https://api.mch.weixin.qq.com/pay/unifiedorder`),传入订单信息。 2. **解析返回的XML数据** 接口返回XML格式数据,需从中提取 `prepay_id`。 3. **生成支付签名** 获取 `prepay_id` 后,结合 `appId`、`timeStamp` 等参数生成签名MD5/HMAC-SHA256)[^4]。 4. **发起支付请求** 将签名参数传给前端,调用微信JSAPI/H5/APP支付接口完成支付。 #### 注意事项 - ⚠️ **回调参数通过流传递**:微信回调参数通过输入流(InputStream)传递,而非传统参数方式[^3]。 - ⚠️ **签名严格排序**:签名参数需按ASCII码从小到大排序(字典序)[^4]。 - ⚠️ **回调地址必填**:必须在请求中设置 `notify_url` 字段接收支付结果[^2]。 #### 代码示例(Java) ```java import java.util.*; public class WeixinPay { // 统一下单接口获取 prepay_id public String getPrepayId() throws Exception { // 1. 构造请求参数 Map<String, String> params = new TreeMap<>(); // 自动按ASCII排序 params.put("appid", "APPID"); params.put("mch_id", "商户号"); params.put("nonce_str", generateNonceStr()); // 随机字符串 params.put("body", "商品描述"); params.put("out_trade_no", "订单号"); params.put("total_fee", "100"); // 单位:分 params.put("spbill_create_ip", "客户端IP"); params.put("notify_url", "回调URL"); // 必须设置[^2] params.put("trade_type", "JSAPI"); // 支付类型 // 2. 生成签名 params.put("sign", generateSign(params, "商户密钥")); // 3. 发送XML请求 String xml = mapToXml(params); String response = HttpClient.post("https://api.mch.weixin.qq.com/pay/unifiedorder", xml); // 4. 解析XML返回 Map<String, String> respMap = parseXml(response); if ("SUCCESS".equals(respMap.get("return_code"))) { return respMap.get("prepay_id"); // 关键数据[^1] } else { throw new Exception("请求失败: " + respMap.get("return_msg")); } } // 生成签名方法 private String generateSign(Map<String, String> params, String key) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } sb.append("key=").append(key); return MD5Util.md5(sb.toString()).toUpperCase(); // MD5加密并转大写[^4] } // 回调处理方法(示例) public void notifyWeiXinPay(HttpServletRequest request) throws Exception { // 从输入流读取回调数据[^3] InputStream is = request.getInputStream(); String xml = IOUtils.toString(is, "UTF-8"); // 解析XML并验证签名 Map<String, String> callbackData = parseXml(xml); if (verifySign(callbackData)) { // 处理支付成功逻辑 } } } ``` #### 关键解析步骤 ```java // XML解析工具方法 private Map<String, String> parseXml(String xml) throws Exception { Map<String, String> map = new HashMap<>(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() .parse(new InputSource(new StringReader(xml))); NodeList children = doc.getDocumentElement().getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node instanceof Element) { map.put(node.getNodeName(), node.getTextContent()); } } return map; } ``` #### 前端支付参数示例 获取 `prepay_id` 后生成前端支付参数模板: ```javascript { appId: "APPID", timeStamp: "当前时间戳", nonceStr: "随机字符串", package: "prepay_id=wx2023...", // 包含prepay_id signType: "MD5", paySign: "按规则生成的签名" // 参数排序后加密[^4] } ``` #### 常见问题处理 1. **`prepay_id` 为空** - 检查商户号和密钥是否正确 - 验证订单金额是否低于最低限额(如1分钱测试订单) - 确保 `notify_url` 为公网可访问URL[^2] 2. **签名失败** - 确认参数按ASCII码排序 - 检查商户密钥是否包含空格 - 使用官方签名工具验证[^4] 3. **回调接收失败** - 参数必须从输入流读取,不可用 `request.getParameter()`[^3] - 返回成功响应:`<xml><return_code>SUCCESS</return_code></xml>` --- ### 相关问题 1. 微信支付回调验签失败有哪些常见原因? 2. 如何调试微信支付沙箱环境? 3. 如何处理微信支付中的重复通知问题? 4. 微信H5支付JSAPI支付在获取 `prepay_id` 时有何区别? [^1]: 接口返回XML数据需解析提取 `prepay_id` [^2]: 回调地址 `notify_url` 必须有效且公网可访问 [^3]: 回调参数通过输入流传递,需特殊处理 [^4]: 签名需严格按ASCII排序后拼接密钥加密
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

li-xun

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

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

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

打赏作者

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

抵扣说明:

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

余额充值