微信公众号支付开发全过程
需求:把游戏里的商城转移到微信公众号上
微信公众号支付开发的准备过程:
需要注册一个公众号,然后开通微信支付功能(需要5个工作日左右),然后就可以得到APPID,APP_SECRET,商户号,商户key等参数。
开始开发:
1. 调用统一下单接口,获取预支付ID
/**
* 用户提交支付,获取微信支付订单接口
*/
@RequestMapping(value="/pay")
public ModelAndView pay(HttpServletRequest request,HttpServletResponse response){
ModelAndView mv = new ModelAndView();
String GZHID = "wxfd7c065eee11112222";// 微信公众号id
String GZHSecret = "b5b3a627f5d1f8888888888888";// 微信公众号密钥id
String SHHID = "111111111";// 财付通商户号
String SHHKEY = "mmmmmmmmmmmmmmm";// 商户号对应的密钥
/*------1.获取参数信息------- */
//商户订单号
String out_trade_no= request.getParameter("state");
//价格
String money = request.getParameter("money");
//金额转化为分为单位
String finalmoney = WeChat.getMoney(money);
//获取用户的code
String code = request.getParameter("code");
/*------2.根据code获取微信用户的openId和access_token------- */
//注: 如果后台程序之前已经得到了用户的openId 可以不需要这一步,直接从存放openId的位置或session中获取就可以。
//toPay.jsp页面中提交的url路径也就不需要再经过微信重定向。写成:http://localhost:8080/项目名/wechat/pay?money=${sumPrice}&state=${orderId}
String openid=null;
try {
List<Object> list = accessToken(code);
openid=list.get(1).toString();
} catch (IOException e) {
logger.error("根据code获取微信用户的openId出现错误", e);
mv.setViewName("error");
return mv;
}
/*------3.生成预支付订单需要的的package数据------- */
//随机数
String nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
//订单生成的机器 IP
String spbill_create_ip = request.getRemoteAddr();
//交易类型 :jsapi代表微信公众号支付
String trade_type = "JSAPI";
//这里notify_url是 微信处理完支付后的回调的应用系统接口url。
String notify_url ="http://69a6a38e.ngrok.natapp.cn/heyi-console/wechat/weixinNotify";
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", GZHID);
packageParams.put("mch_id", SHHID);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", "费用");
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", finalmoney);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);
/*------4.根据package数据生成预支付订单号的签名sign------- */
RequestHandler reqHandler = new RequestHandler(request, response);
reqHandler.init( GZHID, GZHSecret, SHHKEY);
String sign = reqHandler.createSign(packageParams);
/*------5.生成需要提交给统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 的xml数据-------*/
String xml="<xml>"+
"<appid>"+ GZHID+"</appid>"+
"<mch_id>"+ SHHID+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<sign>"+sign+"</sign>"+
"<body><![CDATA["+"费用"+"]]></body>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
"<total_fee>"+finalmoney+"</total_fee>"+
"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
"<notify_url>"+notify_url+"</notify_url>"+
"<trade_type>"+trade_type+"</trade_type>"+
"<openid>"+openid+"</openid>"+
"</xml>";
/*------6.调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 生产预支付订单----------*/
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String prepay_id="";
try {
prepay_id = GetWxOrderno.getPayNo(createOrderURL, xml);
if(prepay_id.equals("")){
mv.addObject("ErrorMsg", "支付错误");
mv.setViewName("error");
return mv;
}
} catch (Exception e) {
logger.error("统一支付接口获取预支付订单出错", e);
mv.setViewName("error");
return mv;
}
/*将prepay_id存到库中*/
PageData p = new PageData();
p.put("shopId", out_trade_no);
p.put("prePayId", prepay_id);
activityService.updatePrePayId(p);
/*------7.将预支付订单的id和其他信息生成签名并一起返回到jsp页面 ------- */
nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String packages = "prepay_id="+prepay_id;
finalpackage.put("appId", GZHID);
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonce_str);
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");
String finalsign = reqHandler.createSign(finalpackage);
mv.addObject("appid", GZHID);
mv.addObject("timeStamp", timestamp);
mv.addObject("nonceStr", nonce_str);
mv.addObject("packageValue", packages);
mv.addObject("paySign", finalsign);
mv.addObject("success","ok");
mv.setViewName("wechat/pay");
return mv;
}
2. H5调起微信支付的内置JS
<script type="text/javascript">
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
"appId" : "<%=paramsMap.get("appId") %>", //公众号名称,由商户传入
"timeStamp":"<%=paramsMap.get("timeStamp") %>", //时间戳,自1970年以来的秒数
"nonceStr" : "<%=paramsMap.get("nonceStr")%>", //随机串
"package" : "<%=paramsMap.get("package")%>",
"signType" : "<%=paramsMap.get("signType")%>", //微信签名方式:
"paySign" : "<%=paramsMap.get("paySign")%>" //微信签名
},
function(res){
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
if(res.err_msg == "get_brand_wcpay_request:ok" ) {//成功
$("#tip_icon").addClass("weui_icon_success");
$("#tip").text("购买成功");
$("#amount").text("恭喜你,获得"+"<%=goodContent%>");
}else if(res.err_msg == "get_brand_wcpay_request:cancel" ) {//取消
$("#tip_icon").addClass("weui_icon_warn");
$("#tip").text("用户取消支付");
}else if(res.err_msg == "get_brand_wcpay_request:fail" ) {//失败
$("#tip_icon").addClass("weui_icon_warn");
$("#tip").text("支付失败");
}else{
$("#tip").text(res.err_msg);
}
$(".confirm_container").show();
$("#chargeBtn").show();
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
3.支付成功之后跳转回自己的系统的某个页面
遇到的问题:
1.在微信商户平台里面需要设置api秘钥(paternerKey),所谓paternerKey意思就是伙伴的秘钥,也就是合作商的秘钥。
需要自定义一个字符串,然后用MD5加密成32位的字符串,然后设置。
2.统一下单接口的容易出错的参数:
1)trade_type==交易类型==取值如下:JSAPI,NATIVE,APP。我们这里使用的JSAPI。标题已经说了,是微信公众号支付。他们的区别,请参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_2。
ps:JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里。MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口。
2)notify_url==通知地址==接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
3)sign==签名==官方给的签名算法。https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3。获取预支付ID时,如果返回值是【签名错误】。那真是的你的签名错了,请仔细核对生成sign的参数名称、参数值和调用统一下单接口的参数名称和参数值
3.要想支付成功,需要在商户号设置支付授权目录。路径应为支付页面的上一级路径。
======================================大功告成=============================================