零、准备工作
首先微信开放平台注册平台账号,接入支付功能还需要开发者资质认证,开发者资质认证需要企业或者个体经商户的一些工商资料,以及手续费300元,成为境内服务商,获得WxMchId,申请开通小程序支付功能,所以对于个人开发者无法接入微信支付。开发者资质认证完成后,创建微信公众平台的小程序开发账号,注意一个邮箱只能创建一种类型账号,也就是说在公众平台注册小程序也需要一个新邮箱。注册成功后创建小程序获得对应的AppId和appSecret,WxAPIKey(需要自己在小程序开放平台设置),微信小程序添加支付功能,绑定主题服务商账号并获得支付功能授权成功,此时微信小程序就获得了调取支付各接口的权限。在微信小程序开发时首先需要获取用户授权,授权后用户登录wx.login()方法将获授权码code,拿到此授权码需要到后台兑取openId,openId除了作为微信用户唯一标识外,在微信支付时还会用到。Java(本文后台接口后续皆为Java示例)后台兑取openId代码:
@RequestMapping(value = "/wxopen.action")
public @ResponseBody String WxOpenId(String code){
//MyHttpUtil为我自己封装的HTTP请求工具类
MyHttpUtil myHttpUtil = new MyHttpUtil();
//调用get请求,请求网址为微信开放接口,需传入参数appid,appSecret,js_code也就是登陆接口获取的code,grant_type固定即可
String res=myHttpUtil.get("https://api.weixin.qq.com/sns/jscode2session?appid="+"你的appid"+"&secret="+"65509019117s515b118866dec6d859ee"+"&js_code="+code +
""+"&grant_type=authorization_code");
return res;
}
一、预下单
小程序传入订单参数,比如支付金额、openId,你还可以传入一些自定义参数作为自己平台的用户订单识别参数。Java后台接口代码如下:
@RequestMapping(value = "/miniwxpay.action")
//PaySendAttach为前台微信小程序传入的参数实体类
public @ResponseBody WxOrderInfo miniwxpay(PaySendAttach paySendAttach,HttpServletRequest rsp) {
//WxOrderInfo为返回到前台微信小程序参数实体类,封装参数如下
/**
* private String appid;
* private String nonce_str;
* private String wxpackage;
* private String mch_id;
* private String prepay_id;
* private String timestamp;
* private String out_trade_no;
*private String sign;
*/
WxOrderInfo wxOrderInfo = new WxOrderInfo();
String result = null;
Map map = new HashMap<>();
String spbill_create_ip;
spbill_create_ip = rsp.getRemoteHost();//获取用户的ip地址。
//微信支付参数实体类,包括WxMchId,WxAPIKey
WXAdvanceOrder wxAdvanceOrder = new WXAdvanceOrder();
wxAdvanceOrder = commonservice.GetWxMessage(paySendAttach.getCompany());
/***********************获取当前年月日时分秒,保证后续订单唯一性***********************/
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String dateNowStr = sdf.format(d);
dateNowStr = "用户编号"+dateNowStr;//此处只要保证订单唯一性即可
//自定义的用户参数json串,在支付回调时可以获取。
String attach_send=
"{\"A\":\""+"参数A"+"\","+
"\"B\":\""+"参数B"+"\","+
"\"C\":\""+"参数C"+"\","+
"\"D\":\""+"参数D"+"\","+
"\"E\":\""+"参数E"+"\"}"
;
/***************************************************************************/
String nonce_str=WXUtil.getNonceStr();//自己封装获取随机字符串,字母和数字组合,长度为20
//stringA为需要加密的请求参数包括appid、attach、body=订单内容、mch_id、nonce_str=随机字符串,notify_url=订单支付结果回调接口、openid、out_trade_no=用户唯一订单编号、spbill_create_ip=用户ip、total_fee=用户支付金额,以分为单位、trade_type=订单请求类型可选APP/JSAPI,微信小程序为JSAPI,key=微信apikey
String stringA="appid="+wxAdvanceOrder.getWxAppid()+"&attach="+attach_send+"&body=水费充值&mch_id="+wxAdvanceOrder.getWxMchId()+"&nonce_str="+nonce_str+"¬ify_url="+companyIp_str+"/AndroidServer/notify.action&openid="+paySendAttach.getOpenId()+"&out_trade_no="+dateNowStr+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+paySendAttach.getMoney()+"&trade_type=JSAPI&key="+wxAdvanceOrder.getWxAPIKey();
//将刚才stringA的参数,主要参数需要保持一致,否则会验签出错拼接成一盒xml字符串,多加一个stringA的加密字符串,此处加密为md5加密
String xmlString = "<xml>\r\n" +
" <appid>"+wxAdvanceOrder.getWxAppid()+"</appid>\r\n" +
" <attach>"+attach_send+"</attach>\r\n" +
" <body>水费充值</body>\r\n" +
" <mch_id>"+wxAdvanceOrder.getWxMchId()+"</mch_id>\r\n" +
" <nonce_str>"+nonce_str+"</nonce_str>\r\n" +
" <notify_url>"+companyIp_str+"/AndroidServer/notify.action</notify_url>\r\n" +
" <openid>"+paySendAttach.getOpenId()+"</openid>\r\n" +
" <out_trade_no>"+dateNowStr+"</out_trade_no>\r\n" +
" <spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>\r\n" +
" <total_fee>"+paySendAttach.getMoney()+"</total_fee>\r\n" +
" <trade_type>JSAPI</trade_type>\r\n" +
" <sign>"+WXUtil.getSign(stringA)+"</sign>\r\n" +
"</xml>";
HttpClient hc=new HttpClient();
//发送微信官方预下单接口请求,主要请求透参数如下,此处请求为XML响应也为xml
PostMethod po = new PostMethod("https://api.mch.weixin.qq.com/pay/unifiedorder");
po.setRequestHeader("Content-Type","text/xml");
po.setRequestHeader("charset","utf-8");
po.setRequestHeader("Accept", "text/xml");
try {
po.setRequestEntity(new StringRequestEntity(xmlString,"text/xml","utf-8"));
} catch (UnsupportedEncodingException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
hc.executeMethod(po);
result = po.getResponseBodyAsString();//服务端返回的Response
result = new String(result.getBytes("ISO-8859-1"), "utf-8");
po.releaseConnection();
map = XMLUtil.doXMLParse(result);//返回的响应xml数据解析
Set set = map.keySet();
//填值
for(Iterator iter = set.iterator(); iter.hasNext();)
{
String key = (String)iter.next();
String value = (String)map.get(key);
if(key.equals("appid")) {
wxOrderInfo.setAppid(value);
}else if(key.equals("mch_id")){
wxOrderInfo.setMch_id(value);
}else if (key.equals("prepay_id")) {
wxOrderInfo.setPrepay_id(value);
}
}
long time=System.currentTimeMillis()/1000;//获取系统时间的10位的时间戳
String strtime=String.valueOf(time);
wxOrderInfo.setNonce_str(WXUtil.getNonceStr());
wxOrderInfo.setWxpackage("Sign=WXPay");//此处必要
wxOrderInfo.setTimestamp(strtime);
wxOrderInfo.setOut_trade_no(dateNowStr);
//将返回数据添加再次加签后生成加密字符串
String stringAgain ="appid="+wxOrderInfo.getAppid()+"&noncestr="+wxOrderInfo.getNonce_str()+"&package=prepay_id="+wxOrderInfo.getPrepay_id()+"&partnerid="+
wxOrderInfo.getMch_id()+"&signType=MD5×tamp="+wxOrderInfo.getTimestamp()+"&key="+wxAdvanceOrder.getWxAPIKey();
wxOrderInfo.setSign(WXUtil.getSign(stringAgain));
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return wxOrderInfo;
}
微信小程序请求miniwxpay的接口需提交data:
data:{
Money: "金额",
OpenId: "openId",
...:"..."
}
二、调起微信支付窗口
微信小程序在预下单成功后,后台接口返回相关数据,然后交由微信自身去解析相关数据和加密数据的正确性。微信官方开放了调起支付的接口wx.requestPayment()。在此接口内传入之前从后台获取到的相关数据,具体代码如下:
wx.requestPayment({
//timeStamp/nonceStr/package参数数据都是直接从miniwxpay接口中获取到的值,signType当然和后台加密方法一致为MD5
timeStamp: data.timestamp,
nonceStr: data.nonce_str,
package: 'prepay_id=' + data.prepay_id,
signType: 'MD5',
//在此处仍需要进行一次MD5加密,具体加密内容如下
paySign: md5.hexMD5("appId=" + data.appid + "&nonceStr=" + data.nonce_str + "&package=prepay_id=" + data.prepay_id + "&signType=MD5&timeStamp=" + data.timestamp +"&key=公众平台设置的APIKEY"),
success(res) { console.log(res)},
fail(res) { console.log(res) }
})
三、接口回调
到上面的步骤完成我们就已经可以调用微信支付窗口成功的进行支付了。当然这还不够,因为更多时候订单的状态是以微信支付异步回调为准的,而且我们可能需要传递一些自己的参数,譬如我需要知道支付成功的用户是谁,我们就可以在回调接口接收自定义参数进行存储。这个处理接口就是我们之前第一步预下单里面的notify_url的值。所以我们需要自己写这样一个接口去处理回调数据。回调接口代码如下:
// 微信异步通知
@RequestMapping(value = "/notify.action")
@ResponseBody
public String notify(HttpServletRequest request, HttpServletResponse response)throws Exception {
//回调数据处理成功返回数据,当微信接口接收到此条数据时表示回调成功,否则继续回调,回调周期依次增大。
String xmlString = "<xml>\r\n" +
" <return_code><![CDATA[SUCCESS]]></return_code>\r\n" +
" <return_msg><![CDATA[OK]]></return_msg>\r\n" +
"</xml>";
Map map = new HashMap<String, Object>();
//回调数据封装实体类,接收回调数据并存入数据库
PayWaterPJ payWaterPJ = new PayWaterPJ();
boolean isSuccess = false;
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/xml;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null)
jb.append(line);
} catch (Exception e) {
}
map = XMLUtil.doXMLParse(jb.toString());
//获取到微信加密数据,为后面比对
String WXSign = (String) map.get("sign");
map.remove("sign");
String result = "";
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(map.entrySet());
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
// 构造签名键值对的格式
String StringA ="";
for (Map.Entry<String, String> item : infoIds) {
String key = item.getKey();
String value = item.getValue();
//将数据拼接为后面生成机密字符串使用
StringA = StringA+key+"="+value+"&";
if(key.equals("total_fee")){
//金额处理
}
else if(key.equals("result_code")) {
isSuccess = true;
}
else if (key.equals("time_end")) {
//订单超时时间
}else if (key.equals("out_trade_no")) {
//订单编号处理
payWaterPJ.setOrderNumber(value);
}else if(key.equals("attach")) {
//到这里你会发现这个参数名字和我们预下单的时候的名字一致,
//没错,传过来的数据就是预下单时自定义数据,
//这就是我们处理的自定义数据
JSONObject jsonObject = new JSONObject(value);
payWaterPJ.setCustomerNumber(jsonObject.getString("A"));
payWaterPJ.setRegionalName(jsonObject.getString("B"));
payWaterPJ.setIDCard(jsonObject.getString("C"));
payWaterPJ.setWaterType(jsonObject.getString("D"));
payWaterPJ.setCustomerName(jsonObject.getString("E"));
}
}
WXAdvanceOrder wxAdvanceOrder = new WXAdvanceOrder();
wxAdvanceOrder = commonservice.GetWxMessage(payWaterPJ.getCompany());
StringA = StringA + "key="+wxAdvanceOrder.getWxAPIKey();
//服务器加密数据,用以和微信回调加密数据比对
String UserSign = WXUtil.getSign(StringA);
if(UserSign.equals(WXSign)) {
//比对微信加密数据和服务器加密数据是否一致,确保数据未被截取更改保证安全性
payWaterPJ.setPayWay("微信");
payWaterPJ.setSynchronize("0");
if(isSuccess) { //订单状态返回成功为真
//保存订单
int re = userservice.PayWater(payWaterPJ);
if(re==1) {
//保存成功,回复消息
return xmlString;
}else {
return null;
}
}
else {
return null;
}
}else {
return null;
}
}
四、总结
微信小程序接入微信支付流程大体如上,大的来说分为四个准备参数、三个步骤和两个算法。
四个准备参数指的是需要创建小程序后固定的四个参数,分别是AppId和appSecret,WxMchId,WxAPIKey。
三个步骤则指的是预下单–>微信小程序调用微信支付–>支付结果回调。
两个算法指的是MD5加密算法和随机字符串生成算法。其中在后台预下单接口MD5两次,小程序MD5一次,回调又MD5一次,需要注意的是每次MD5加密的数据不能出错,不然会调用微信接口验签失败。
来源网络,侵权联系删除
版权声明:【微信小程序支付功能开发前端是什么,小程序接入微信支付】版权归原作者所有,本文由作者:【王鹏(python工程师)】用户自发贡献上传,该文观点仅代表作者本人,本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任,如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至举报,一经查实,本站将立刻删除,如若转载,请注明出处:https://www.intostarry.com/jrzy/447.html