微信的扫码支付,web(h5)支付,小程序支付,代码干货
程序员文章站
2022-07-13 17:08:33
...
代码都没有经过精简,是为了看起来方便,有需要可以自行修改封装
扫码支付比较简单
/**
* 统一下单
* @throws Exception## 标题
*/
@RequestMapping("/wxpay/unified/order")
@ResponseBody
public Map<String, String> doUnifiedOrder(
Model model) throws Exception {
Map<String, String> resp = null;
//wxpayConfig是自行封装的一个类 下面会放出具体属性
WxPayConfig wxPayConfig =new WxPayConfig (“可以把支付信息存库,需要用到都从库里拿”);
//最好对数据库查询出的信息进行一下基本的判空
if (config == null) {
throw new Exception("config is null");
}
if (config.getAppId() == null || config.getAppId().trim().length() == 0) {
throw new Exception("appid in config is empty");
}
if (config.getMchId() == null || config.getMchId().trim().length() == 0) {
throw new Exception("appid in config is empty");
}
if (config.getSslCert() == null) {
throw new Exception("cert stream in config is empty");
}
WxPay wxpay = new WxPay(wxPayConfig);
//以下四个参数要根据具体需求使用 也可以都写死 只传orderId 和金额 这样是为了封装起来 使用方便
String orderId=request.getParameter("order_id");
String outTradeNo=request.getParameter("out_trade_no");
String totalFee=request.getParameter("total_fee");
String body_desc=request.getParameter("body_desc");
logger.info("支付提交参数:outTradeNo"+outTradeNo+"orderId:"+orderId+"totalFee:"+totalFee+"body_desc:"+body_desc);
Map<String, String> data = new HashMap<String, String>();
data.put("body", body_desc);//支付描述
data.put("out_trade_no", outTradeNo);//订单号
data.put("device_info", "");//设备号
data.put("fee_type", "CNY");//人民币类型
data.put("total_fee", totalFee);//总金额
String ip=“”;
//获取ip
InetAddress ia=null;
try {
ia=ia.getLocalHost();
ip=ia.getHostAddress();
}catch (Exception e) {
e.printStackTrace();
}
data.put("spbill_create_ip",ip);//IP地址
data.put("notify_url", "https://xxxx.com/xxx/xx");//完整的微信回调通知地址 需要在微信公众号后台配置
data.put("product_id", orderId);
data.put("trade_type", "NATIVE"); // 此处指定为扫码支付 0
data.put("attach", orderId);
try {
logger.info("支付参数为:"+data);
//发起请求支付了 resp为返回信息
resp = wxpay.unifiedOrder(data);
//WxPayLog是微信支付日志的记录 有需要可以加上 就不放出来了
WxPayLog wxPayLog=new WxPayLog();
wxPayLog.setOrderId(Long.parseLong(orderId));
wxPayLog.setCodeUrl(resp.get("code_url"));
wxPayLog.setCreateTime(new Date());
wxPayLog.setNonceStr(resp.get("nonce_str"));
wxPayLog.setOutTradeNo(outTradeNo);
wxPayLog.setResultCode(resp.get("result_code"));
wxPayLog.setReturnMsg(resp.get("return_msg"));
wxPayLog.setSign(resp.get("sign"));
wxPayLog.setTradeType(0);
wxPayLog.setTotalFee(Double.parseDouble(totalFee));
wxPayLogRep.save(wxPayLog);
} catch (Exception e) {
e.printStackTrace();
}
logger.info("支付返回:"+resp);
return resp;
}
WxPayConfig 实体类
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
*
*封装支付需要的相关参数
**/
@Entity
public class WxPayConfig {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long Id;
private String appId;//公众账号ID
private String appSecret;//公众号秘钥
private String mchId;//商户号
private String appKey;//app秘钥
private String isvName;//服务商名称
private String subMchId;//子商户号
private Integer payType;//支付类型 0.通过I,SV渠道接入 HMAC-SHA256 (默认) 1. *支付渠道 MD5,
//get set 方法补一下
}
在回调函数 也就是传给微信的‘notify_url’路径所对应的方法中,微信会回调多次这个地址,在收到支付成功的消息,需要给微信返回已收到,不然会一直回调,具体次数不太记得了。时间相差也懒得去百度了
扫码支付的微信回调方法
/**
* 微信支付回调处理
* @return
* @throws Exception
*/
@RequestMapping("/wxpay/notify")//具体url自行在发起支付的时候配置
@ResponseBody
public String wxNotify() throws Exception {
logger.info("微信回调了");
//获取微信传回的信息
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
InputStream in = request.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
in.close();
String content = new String(out.toByteArray(), "utf-8");// 微信传回的信息是xml数据
Map<String, String> map =xmlToMap(content);
String resultCode = map.get("result_code");
String outTradeNo=map.get("out_trade_no");
String totalFee=map.get("total_fee");
logger.info("微信返回数据:"+"resultCode:"+resultCode+"outTradeNo:"+outTradeNo+"totalFee:"+totalFee);
WxPayLog payLog = new WxPayLog (‘找到支付之前存库的WxPayLog ,可以使用outTradeNo去库里查找’);
// 可以根据当前payLog.getOrderId()去判断是哪一个支付的订单 或者具体是用哪一个参数去做的订单绑定都可以
logger.info("订单Id:"+payLog.getOrderId());
if (resultCode.equalsIgnoreCase("FAIL")) {//支付失败
//将相对应订单状态修改一下,并返回给客户 ,暂时没遇到过支付失败,不过不代表没有
} else if (resultCode.equalsIgnoreCase("SUCCESS")) {// 支付成功
logger.info("支付成功了");
if(‘对比订单金额和微信返回金额是否一致‘) {
}else {
//支付金额不一致!
}
// 这里处理自己数据库中的订单表业务
logger.info("返回数据为:"+JSON.toJSONString(map));
//支付成功通知微信已接受到返回信息
if(resultCode.equalsIgnoreCase("SUCCESS")) {
String resSuccessXml = "<xml><return_code>SUCCESS</return_code></xml>";
return resSuccessXml;
}
String resFailXml = "<xml><return_code>FAIL</return_code></xml>";
return resFailXml;
}
xml转换成map的方法
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
throw ex;
}
}
扫码支付差不多就就这样,如果有坑可以留言
接下来web支付和小程序支付,两者几乎没差别,写在一起
/**
*web, 小程序支付
* @param request
* @return
*/
@RequestMapping(value="/wx/wxPayment")
@ResponseBody
public JSON payOut(HttpServletRequest request) {
JSONObject map= new JSONObject();
logger.info("进入微信支付请求");
//可以继续使用上文的 WxPayConfig
WxPayConfig wxPayConfig= new WxPayConfig ();
if (wxConfig == null) {
map.put("code", 0);
map.put("msg", "微信配置错误");
return map;
}
String wxAppId = wxPayConfig.getWxAppId();
String mchId = wxPayConfig.getMchId();
String key = wxPayConfig.getPayKey();
try {
String appSecret=wxPayConfig.getWxAppSecret();
Random random = new Random();
String nonceStr = "";
while (nonceStr.length() < 32) {
int i = random.nextInt(35);
nonceStr += randomStr[i];
}
SimpleDateFormat sdf2=new SimpleDateFormat("yyyyMMddHHmmss");
String orderId =“”;//orderId可以用时间加上一个用户的id 这里随意 但是要保证微信回调的时候可以判断是哪一个用户发起的支付
String openId=“”;//对应用户openId
String body = "";// 此处填告知用户支付了什么业务 比如某某超市什么的,中文有可能报错,如果报错就使用英文试试排除这个错误,不过下文也有对参数进行编码,一般不会出错
String totalFee =String.valueOf(product.getTotal());
//金额单位转换为“分”
float floatTotalFee = Float.parseFloat(totalFee);
int intTotalFee = (int) (floatTotalFee * 100);
totalFee = "" + intTotalFee;
String tradeType = "JSAPI";
String spBillCreateIP = request.getRemoteAddr();
//设置回调地址
String notifyUrl = "";//和上文扫码支付一样的配置就好了,要注意商户后台的支付域名配置 最好是
/*
比如支付是:https://xxxx.com/wxpay/payment
那回调的就要是:https://xxxx.com/wxpay/notify
商户后台的支付域名配置:https://xxxx.com/wxpay 就要这样配置到最后"/"的上一级
*/
String sign =WxPayUtil.MD5("appid=" + wxAppId + "&body=" + body + "&mch_id=" + mchId + "&nonce_str=" + nonceStr
+ "¬ify_url=" + notifyUrl + "&openid=" + openId + "&out_trade_no=" +orderId
+ "&spbill_create_ip=" + spBillCreateIP + "&total_fee=" + totalFee + "&trade_type=" + tradeType
+ "&key=" + key);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 创建Document
Document document = builder.newDocument();
// 创建Element
Element xml = document.createElement("xml");
xml.appendChild(makeElement("appid", wxAppId, document));
xml.appendChild(makeElement("body", body, document));
xml.appendChild(makeElement("mch_id", mchId, document));
xml.appendChild(makeElement("nonce_str", nonceStr, document));
xml.appendChild(makeElement("notify_url", notifyUrl, document));
xml.appendChild(makeElement("openid", openId, document));
xml.appendChild(makeElement("out_trade_no", orderId, document));
xml.appendChild(makeElement("spbill_create_ip", spBillCreateIP, document));
xml.appendChild(makeElement("total_fee", totalFee, document));
xml.appendChild(makeElement("trade_type", tradeType, document));
xml.appendChild(makeElement("sign", sign, document));
document.appendChild(xml);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.setOutputProperty("encoding", "UTF-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
t.transform(new DOMSource(document), new StreamResult(bos));
String xmlStr = bos.toString("UTF-8");
// 发送支付请求 HttpUtils 这个共具类很普遍 有需要请自行百度
String result = HttpUtils.doPost("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlStr);
logger.info("微信支付返回:"+result);
// 解析
StringReader sr = new StringReader(result);
InputSource is = new InputSource(sr);
DocumentBuilderFactory factory2 = DocumentBuilderFactory.newInstance();
DocumentBuilder builder2 = factory2.newDocumentBuilder();
Document doc = builder2.parse(is);
String returnCode = doc.getElementsByTagName("return_code").item(0).getFirstChild().getNodeValue();
System.out.println("returnCode:"+returnCode);
//web,和小程序支付都会有一个预订单Id prepay_id
if (returnCode.equals("SUCCESS")) {
String prepayId = doc.getElementsByTagName("prepay_id").item(0).getFirstChild().getNodeValue();//getChileElementValue(element, "prepay_id");
map.put("appId", wxAppId);
String timeStamp = Long.toString(System.currentTimeMillis());
timeStamp = timeStamp.substring(0, 10);
map.put("timeStamp", timeStamp);
map.put("nonceStr", nonceStr);
String packageStr = "prepay_id=" + prepayId;
map.put("package", packageStr);
map.put("signType", "MD5");
//MD5加密 具体规则查询微信支付API
String paySign = MD5("appId=" + wxAppId + "&nonceStr=" + nonceStr + "&package=" + packageStr
+ "&signType=MD5&timeStamp=" + timeStamp + "&key=" + key);
map.put("paySign", paySign);
logger.info("调起支付:"+map);
/* 小程序支付
到这一步将map这个参数传给小程序端,小程序端根据以上参数就可以发起支付
返回签名错误的时候,就要检查paySign 参数是否有错,参数可以打印出来看看 方便找错
*/
/* web支付
将map转换成json传到界面
*/
return map;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("catch");
}
map.put("code", 0);
map.put("msg", "系统错误");
return map;
}
web(h5)支付js
//先判断是否在微信环境下
if (typeof WeixinJSBridge == "undefined"){
alert("请在微信中打开网站进行支付");
if( document.addEventListener ){
//addEventListener() 方法用于向指定元素添加事件句柄。
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
//使用ajax访问上文的支付接口 返回的信息调用下文方法
function jsapiPay (payment){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":payment.appId, //公众号名称,由商户传入
"timeStamp":payment.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr":payment.nonceStr, //随机串
"package":payment.package1,
"signType":"MD5", //微信签名方式:
"paySign":payment.paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
//表示支付成功
//更新相对应支付订单
} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
web支付,小程序支付回调函数
/**
* 微信支付回调
* @param request
* @return
*/
@RequestMapping(value="/wxPayNotify")
public void wxNotify(HttpServletRequest request, HttpServletResponse response) {
logger.info("微信支付回调");
String postData = "";
try {
BufferedReader reader = null;
StringBuilder buf = new StringBuilder();
try {
String line;
reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
while ((line = reader.readLine()) != null) {
buf.append(line).append("\n");
}
} catch (Exception e) {
throw e;
}
postData = buf.toString();
logger.info("微信支付回调结果:"+postData);
} catch (Exception e) {
return ;//"success";
}
if (postData != null && !postData.equals("")) {
StringReader sr = new StringReader(postData);
InputSource is = new InputSource(sr);
DocumentBuilderFactory factory2 = DocumentBuilderFactory.newInstance();
DocumentBuilder builder2 = null;
try {
builder2 = factory2.newDocumentBuilder();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Document doc = null;
try {
doc = builder2.parse(is);
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String returnCode = doc.getElementsByTagName("return_code").item(0).getFirstChild().getNodeValue();
System.out.println("支付成功返回:"+doc);
if (returnCode.equalsIgnoreCase("SUCCESS")) {
// ͨ解析
String resultCode = doc.getElementsByTagName("result_code").item(0).getFirstChild().getNodeValue();
String orderId = doc.getElementsByTagName("out_trade_no").item(0).getFirstChild().getNodeValue();
//支付是否成功
if (resultCode.equalsIgnoreCase("success")) {
String merchantId =doc.getElementsByTagName("mch_id").item(0).getFirstChild().getNodeValue();
String wxOrderId =doc.getElementsByTagName("transaction_id").item(0).getFirstChild().getNodeValue();
String payTime =doc.getElementsByTagName("time_end").item(0).getFirstChild().getNodeValue();
String tradeType =doc.getElementsByTagName("trade_type").item(0).getFirstChild().getNodeValue();
String bankType =doc.getElementsByTagName("bank_type").item(0).getFirstChild().getNodeValue();
String totalFee =doc.getElementsByTagName("total_fee").item(0).getFirstChild().getNodeValue();
String feeType = doc.getElementsByTagName("fee_type").item(0).getFirstChild().getNodeValue();
String cashFee = doc.getElementsByTagName("cash_fee").item(0).getFirstChild().getNodeValue();
String cashFeeType = "CNY";//doc.getElementsByTagName("cash_fee_type").item(0).getFirstChild().getNodeValue();
String sign = doc.getElementsByTagName("sign").item(0).getFirstChild().getNodeValue();
/**
进行相关订单操作和验证
*/
//获取签名要用到的key
WxPayConfig wxPayConfig = new WxPayConfig ('支付的相关参数');
if (wxPayConfig == null) {
return ;
}
String mchId = wxPayConfig.getMch_Id();
String key = wxPayConfig.getPay_Key();
//签名认证
Map<String , String> map = new HashMap<String,String>();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
map.put(element.getNodeName(), element.getTextContent());
}
}
String newSign = generateSignature(map, key);
if(!sign.equals(newSign)) {
return ;
}
payTime = payTime.substring(0, 4) + "-" + payTime.substring(4, 6) + "-" + payTime.substring(6, 8)
+ " " + payTime.substring(8, 10) + ":" + payTime.substring(10, 12) + ":"
+ payTime.substring(12, 14);
java.sql.Timestamp timeStamp = java.sql.Timestamp.valueOf(payTime);
// 根据具体支付流程去处理支付成功后的操作
//支付日志存库
WxPayNotify wxPayNotify = new WxPayNotify ();
wxPayNotify.setMerchantId(merchantId);
wxPayNotify.setWxOrderId(wxOrderId);
wxPayNotify.setPayTime(timeStamp);
wxPayNotify.setTradeType(tradeType);
wxPayNotify.setBankType(bankType);
wxPayNotify.setTotalFee(totalFee);
wxPayNotify.setFeeType(feeType);
wxPayNotify.setCashFee(cashFee);
wxPayNotify.setFeeType(cashFeeType);
wxPayNotify.setOrderId(orderId);
//保存支付日志
//修改订单状态
response.setContentType("text/plain");
try {
//告知微信 收到支付成功的消息
Writer out = response.getWriter();
out.write("<xml>");
out.write("<return_code><![CDATA[SUCCESS]]></return_code>");
out.write("<return_msg><![CDATA[OK]]></return_msg>");
out.write("</xml>");
out.flush();
} catch (IOException e) {
}
} else {
}
}
}
return ;
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
* @param data 待签名数据
* @param key API**
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals("sign")) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
return MD5(sb.toString()).toUpperCase();
}
有问题请留言,勿喷,只是为了记录一下微信支付注意事项
上一篇: css3的animation动画属性实现闹钟左右摇摆
下一篇: ios的qq分享接入流程