用最近在学习使用java开发微信公众号,在学习过程中踩了很多坑,确实费了不少劲,先来分享一下微信公众号的支付流程,已提供给朦朦胧胧的小伙伴们看看,顺便给自己整理了一下以便后续使用,这是博主第一次写博客,有毛病的地方还望指出来,谢谢!
本此开发使用的框架为springboot,开发分为三个步骤。
在开发前强烈推荐各位客官先看看开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
一 定 要 看!!!
一、准备阶段
具备条件:已认证微信号,且通过微信支付认证,这个可以看微信文档,会有详细的解析,这里就不再重复了。
开发前,我们先登录自己的服务号,点击微信支付——>开发配置(目前开发配置已经迁移到商户平台了)
先进入商户平台—->产品中心—->开发配置
最下面有这个支付配置框,请配置好支付授权目录,这里的目录为 支付html页面所对应的url路径(文件路径)
配置API密匙,在商户平台中账户中心—->API安全
接下来要从公众号—->基本配置,拿到微信支付中所需要使用的参数AppId、AppSecret
二、前端页面
下面是我在做支付测试时使用的测试页面,为了方便,只做了一个支付按钮并且有点奇怪,直接上代码吧!
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="javascript:void(0);" onclick="pay();" class="button">支付</a>
</body>
</html>
//这儿是相关的JS代码
<script type="text/javascript" src="../js/jquery-1.10.2.min.js"></script>
<script type="application/javascript">
var appid;
var nonceStr;
var myPackage = "prepay_id=";
var tmp ;
var sign;
function pay() {
$.ajax({
url: "/web/wxpay/"+1000,
async: false,
dataType: "json",
success: function(resp) {
appid=resp.appId;
nonceStr=resp.nonceStr;
tmp=resp.timeStamp;
myPackage=myPackage+resp.pg;
sign=resp.paySign;
alert(appid);
alert(nonceStr);
alert(tmp);
callpay();
}
});
}
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": appid, //公众号名称,由商户传入
"timeStamp": tmp, //时间戳,自1970年以来的秒数
"nonceStr": nonceStr, //随机串
"package": myPackage,
"signType": "MD5", //微信签名方式:
"paySign": sign //微信签名
},
function(res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
alert('支付成功');
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
alert('支付过程中用户取消');
} else if (res.err_msg == "get_brand_wcpay_request:fail") {
alert('支付失败');
} else {
alert('未知异常');
} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
function callpay() {
if (typeof WeixinJSBridge == "undefined") {
alert("WeixinJSBridge");
if (document.addEventListener) {
alert("addEventListener");
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
}
</script>
页面结果:
哈哈哈,当然不是这个按钮啦,这是项目中的按钮,别介意,作用还是差不多的!!
三、后端实现
首先,需要下载微信公众平台上提供的SDK,然后将SDK打成jar包,并在pom.xml添加依赖。
这是博主使用来统一下单的类
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.zt.dao.PrintDao;
import com.zt.dao.UserDao;
import com.zt.domain.User;
public class MyWXPay {
public String strJson;
public Map<String, String> resp = new HashMap<String, String>();
public Map<String, String> payMap = new HashMap<String, String>();
public String str=getOutTradeNo();
public MyWXPay(HttpServletRequest request,String openid,BigDecimal money,long useIntegral) throws Exception {
// WXPayUtil wxPayUtile=new WXPayUtil();
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
//这里生产预支付订单签名
Map<String, String> data = new HashMap<String, String>();
String theMoney =String.valueOf(money.multiply(new BigDecimal(100)).intValue()) ;//微信支付 total_fee不能出现小数 必须转换为分以整数形式传递数据
String timeStampStr=String.valueOf(System.currentTimeMillis());
String timeStamp=timeStampStr.substring(0, 10);
data.put("appid","");//填写AppId
data.put("mch_id","");//填写商户号
data.put("body", "");//商品描述
data.put("out_trade_no", str);//商户订单号,博主是用随机生成的方式生成
data.put("device_info", "WEB");//设备号
data.put("fee_type", "CNY");//人名币类型
data.put("total_fee", theMoney);//支付金额
data.put("openid", openid);//支付者的openId
data.put("timeStamp", timeStamp);//时间戳
data.put("spbill_create_ip", request.getRemoteAddr());//终端IP
data.put("notify_url", "http://wechat.gdssn.top/web/notify");//通知地址
data.put("nonce_str", str);//随机字符串
data.put("trade_type", "JSAPI"); // 此处指定为公众号支付
//这里生成支付订单
try {
resp = wxpay.unifiedOrder(data);
String timeStampStr2=String.valueOf(System.currentTimeMillis());
String timeStamp2=timeStampStr2.substring(0, 10);
payMap.put("appId", config.getAppID());
payMap.put("timeStamp", timeStamp2);
payMap.put("nonceStr", getOutTradeNo());
payMap.put("signType", "MD5");
payMap.put("package", "prepay_id=" + resp.get("prepay_id"));
String paySign = WXPayUtil.generateSignature(payMap, config.getKey());
payMap.put("pg", resp.get("prepay_id"));
payMap.put("paySign", paySign);
strJson = JSON.toJSONString(payMap);
System.out.println("resp="+resp);
System.out.println("strJson="+strJson);
} catch (Exception e) {
e.printStackTrace();
}
}
//生成随机的32位字符串,用于商户订单号,随机字符串
public static String getOutTradeNo() {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 32; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
这是博主自己重写的配置参数的类,上面的类中的AppId、mchId等等参数是从这里获取的
import com.github.wxpay.sdk.WXPayConfig;
import java.io.*;
public class MyConfig implements WXPayConfig{
public String keyPath ="";
public String mchId="";
public String ApiSecret="";
public String appId="";
private byte[] certData;
public MyConfig() throws Exception {
File file = new File(keyPath);
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int) file.length()];
certStream.read(this.certData);
certStream.close();
}
public String getAppID() {
return appId;
}
public String getMchID() {
return mchId;
}
public String getKey() {
return ApiSecret;
}
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
public int getHttpConnectTimeoutMs() {
return 8000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
}
最后是编写微信支付的接口
String result;//返回给微信的回调数据
MyWXPayRefund myWXPayRefund;
@RequestMapping("/wxpay/{money:.+}") //{money:.+} 这种写法可以接收小数点!
public void WxPay(@PathVariable(name = "money", required = false)String money,HttpServletRequest request,HttpServletResponse response,ModelMap model){
String openId=request.getSession().getAttribute("openid").toString();
MyWXPay myWxPay;
BigDecimal theMoneyBD=new BigDecimal(money);
BigDecimal theIntegral=new BigDecimal(0);
theMoneyBD=theMoneyBD.setScale(2, BigDecimal.ROUND_UNNECESSARY );
try {
myWxPay = new MyWXPay(request,openId,theMoneyBD.subtract(theIntegral));
CommonUtils.writeJson(myWxPay.payMap, response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@RequestMapping("/notify")
public String notify(@RequestBody String notifyData) {
// String notifyData = "...."; // 支付结果通知的xml格式数据
try{
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
Map<String,String> resultData=new HashMap<>();
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map
if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
LOGGER.info("签名正确!");
// 签名正确
// 进行处理。
// 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
}
else {
resultData.put("return_code", "FAIL");
resultData.put("return_msg", "签名失败");
result = WXPayUtil.mapToXml(resultData);
// 签名错误,如果数据里没有sign字段,也认为是签名错误
}
if(notifyMap.get("result_code").equals("SUCCESS")){
myWXPayRefund=new MyWXPayRefund(notifyMap);
if(myWXPayRefund.resp.get("isSuccess") != null&&myWXPayRefund.resp.get("isSuccess").equals("true")){
LOGGER.info("交易状态成功!return_code= "+notifyMap.get("result_code"));
resultData.put("return_code", "SUCCESS");
resultData.put("return_msg", "OK");
result = WXPayUtil.mapToXml(resultData);
return result;
}else{
LOGGER.info("交易状态失败!return_code= "+notifyMap.get("result_code"));
resultData.put("return_code", "fail");
resultData.put("return_msg", "交易失败");
result = WXPayUtil.mapToXml(resultData);
}
}
if(!notifyMap.get("result_code").equals("SUCCESS")){
LOGGER.info("交易状态失败!return_code= "+notifyMap.get("result_code"));
resultData.put("return_code", "fail");
resultData.put("return_msg", "参数格式校验错误");
result = WXPayUtil.mapToXml(resultData);
}
LOGGER.info("返回给微信后台的result= "+result);
}catch(Exception e){
e.printStackTrace();
}
return result;
}
点击按钮结果:
到这里就结束啦,如有疑问或有不对的地方请留言,博主会进行解答与改正,谢谢!!