微信小程序支付(java后端)
第一步
进入小程序,下单,请求下单支付,调用小程序登录API来获取Openid(https://mp.weixin.qq.com/debug/w....html#wxloginobject),生成商户订单,这些都是在小程序端完成的业务。
小程序端代码
//pages/pay/pay.js
varapp=getApp();
Page({
data:{},
onLoad:function(options){
//页面初始化options为页面跳转所带来的参数
},
/*微信支付*/
wxpay:function(){
varthat=this
//登陆获取code
wx.login({
success:function(res){
console.log(res.code)
//获取openid
that.getOpenId(res.code)
}
});
},
getOpenId:function(code){
varthat=this;
wx.request({
url:"https://api.weixin.qq.com/sns/jscode2session?appid=wxa142513e524e496c&secret=5d6a7d86048884e7c60f84f7aa85253c&js_code="+code+"&grant_type=authorization_code",
data:{},
method:'GET',
success:function(res){
console.log('返回openId')
console.log(res.data)
that.generateOrder(res.data.openid)
},
fail:function(){
//fail
},
complete:function(){
//complete
}
})
},
/**生成商户订单*/
generateOrder:function(openid){
varthat=this
//统一支付
wx.request({
url:'http://localhost:8070/RMS/pay_pay.action',
method:'GET',
data:{
total_fee:'5',
body:'支付测试',
attach:'真假酒水'
},
success:function(res){
console.log(res)
varpay=res.data
//发起支付
vartimeStamp=pay[0].timeStamp;
console.log("timeStamp:"+timeStamp)
varpackages=pay[0].package;
console.log("package:"+packages)
varpaySign=pay[0].paySign;
console.log("paySign:"+paySign)
varnonceStr=pay[0].nonceStr;
console.log("nonceStr:"+nonceStr)
varparam={"timeStamp":timeStamp,"package":packages,"paySign":paySign,"signType":"MD5","nonceStr":nonceStr};
that.pay(param)
},
})
},
/*支付*/
pay:function(param){
console.log("支付")
console.log(param)
wx.requestPayment({
timeStamp:param.timeStamp,
nonceStr:param.nonceStr,
package:param.package,
signType:param.signType,
paySign:param.paySign,
success:function(res){
//success
console.log("支付")
console.log(res)
wx.navigateBack({
delta:1,//回退前delta(默认为1)页面
success:function(res){
wx.showToast({
title:'支付成功',
icon:'success',
duration:2000
})
},
fail:function(){
//fail
},
complete:function(){
//complete
}
})
},
fail:function(res){
//fail
console.log("支付失败")
console.log(res)
},
complete:function(){
//complete
console.log("paycomplete")
}
})
}
})
复制代码
第二步
调用支付统一下单API来获取prepay_id,并将小程序调起支付数据需要签名的字段appId,timeStamp,nonceStr,package再次签名(https://pay.weixin.qq.com/wiki/d...ter=7_7&index=3)
后台代码
packagecn.it.shop.action;
importjava.io.ByteArrayInputStream;
importjava.io.InputStream;
importjava.io.UnsupportedEncodingException;
importjava.text.SimpleDateFormat;
importjava.util.Date;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importorg.dom4j.Document;
importorg.dom4j.DocumentException;
importorg.dom4j.Element;
importorg.dom4j.io.SAXReader;
importcn.it.shop.util.MessageUtil;
importcn.it.shop.util.PayUtil;
importcn.it.shop.util.PaymentPo;
importcn.it.shop.util.UUIDHexGenerator;
importnet.sf.json.JSONArray;
importnet.sf.json.JSONObject;
/**
*@author
*@version创建时间:2017年1月21日下午4:59:03
*小程序端请求的后台action,返回签名后的数据传到前台
*/
publicclassPayAction{
privateStringtotal_fee;//总金额
privateStringbody;//商品描述
privateStringdetail;//商品详情
privateStringattach;//附加数据
privateStringtime_start;//交易起始时间
privateStringtime_expire;//交易结束时间
privateStringopenid;//用户标识
privateJSONArrayjsonArray=newJSONArray();
publicStringpay()throwsUnsupportedEncodingException,DocumentException{
body=newString(body.getBytes("UTF-8"),"ISO-8859-1");
Stringappid="替换为自己的小程序ID";//小程序ID
Stringmch_id="替换为自己的商户号";//商户号
Stringnonce_str=UUIDHexGenerator.generate();//随机字符串
Stringtoday=newSimpleDateFormat("yyyyMMddHHmmss").format(newDate());
Stringcode=PayUtil.createCode(8);
Stringout_trade_no=mch_id+today+code;//商户订单号
Stringspbill_create_ip="替换为自己的终端IP";//终端IP
Stringnotify_url="http://www.weixin.qq.com/wxpay/pay.php";//通知地址
Stringtrade_type="JSAPI";//交易类型
Stringopenid="替换为用户的openid";//用户标识
/**/
PaymentPopaymentPo=newPaymentPo();
paymentPo.setAppid(appid);
paymentPo.setMch_id(mch_id);
paymentPo.setNonce_str(nonce_str);
Stringnewbody=newString(body.getBytes("ISO-8859-1"),"UTF-8");//以utf-8编码放入paymentPo,微信支付要求字符编码统一采用UTF-8字符编码
paymentPo.setBody(newbody);
paymentPo.setOut_trade_no(out_trade_no);
paymentPo.setTotal_fee(total_fee);
paymentPo.setSpbill_create_ip(spbill_create_ip);
paymentPo.setNotify_url(notify_url);
paymentPo.setTrade_type(trade_type);
paymentPo.setOpenid(openid);
//把请求参数打包成数组
MapsParaTemp=newHashMap();
sParaTemp.put("appid",paymentPo.getAppid());
sParaTemp.put("mch_id",paymentPo.getMch_id());
sParaTemp.put("nonce_str",paymentPo.getNonce_str());
sParaTemp.put("body",paymentPo.getBody());
sParaTemp.put("out_trade_no",paymentPo.getOut_trade_no());
sParaTemp.put("total_fee",paymentPo.getTotal_fee());
sParaTemp.put("spbill_create_ip",paymentPo.getSpbill_create_ip());
sParaTemp.put("notify_url",paymentPo.getNotify_url());
sParaTemp.put("trade_type",paymentPo.getTrade_type());
sParaTemp.put("openid",paymentPo.getOpenid());
//除去数组中的空值和签名参数
MapsPara=PayUtil.paraFilter(sParaTemp);
Stringprestr=PayUtil.createLinkString(sPara);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
Stringkey="&key=替换为商户支付密钥";//商户支付密钥
//MD5运算生成签名
Stringmysign=PayUtil.sign(prestr,key,"utf-8").toUpperCase();
paymentPo.setSign(mysign);
//打包要发送的xml
StringrespXml=MessageUtil.messageToXML(paymentPo);
//打印respXml发现,得到的xml中有“__”不对,应该替换成“_”
respXml=respXml.replace("__","_");
Stringurl="https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单API接口链接
Stringparam=respXml;
//Stringresult=SendRequestForUrl.sendRequest(url,param);//发起请求
Stringresult=PayUtil.httpRequest(url,"POST",param);
//将解析结果存储在HashMap中
Mapmap=newHashMap();
InputStreamin=newByteArrayInputStream(result.getBytes());
//读取输入流
SAXReaderreader=newSAXReader();
Documentdocument=reader.read(in);
//得到xml根元素
Elementroot=document.getRootElement();
//得到根元素的所有子节点
@SuppressWarnings("unchecked")
ListelementList=root.elements();
for(Elementelement:elementList){
map.put(element.getName(),element.getText());
}
//返回信息
Stringreturn_code=map.get("return_code");//返回状态码
Stringreturn_msg=map.get("return_msg");//返回信息
System.out.println("return_msg"+return_msg);
JSONObjectJsonObject=newJSONObject();
if(return_code=="SUCCESS"||return_code.equals(return_code)){
//业务结果
Stringprepay_id=map.get("prepay_id");//返回的预付单信息
StringnonceStr=UUIDHexGenerator.generate();
JsonObject.put("nonceStr",nonceStr);
JsonObject.put("package","prepay_id="+prepay_id);
LongtimeStamp=System.currentTimeMillis()/1000;
JsonObject.put("timeStamp",timeStamp+"");
StringstringSignTemp="appid="+appid+"&nonceStr="+nonceStr+"&package=prepay_id="+prepay_id+"&signType=MD5&timeStamp="+timeStamp;
//再次签名
StringpaySign=PayUtil.sign(stringSignTemp,"&key=替换为自己的密钥","utf-8").toUpperCase();
JsonObject.put("paySign",paySign);
jsonArray.add(JsonObject);
}
return"pay";
}
publicStringgetTotal_fee(){
returntotal_fee;
}
publicvoidsetTotal_fee(Stringtotal_fee){
this.total_fee=total_fee;
}
publicStringgetBody(){
returnbody;
}
publicvoidsetBody(Stringbody){
this.body=body;
}
publicJSONArraygetJsonArray(){
returnjsonArray;
}
publicvoidsetJsonArray(JSONArrayjsonArray){
this.jsonArray=jsonArray;
}
publicStringgetDetail(){
returndetail;
}
publicvoidsetDetail(Stringdetail){
this.detail=detail;
}
publicStringgetAttach(){
returnattach;
}
publicvoidsetAttach(Stringattach){
this.attach=attach;
}
publicStringgetTime_start(){
returntime_start;
}
publicvoidsetTime_start(Stringtime_start){
this.time_start=time_start;
}
publicStringgetTime_expire(){
returntime_expire;
}
publicvoidsetTime_expire(Stringtime_expire){
this.time_expire=time_expire;
}
publicStringgetOpenid(){
returnopenid;
}
publicvoidsetOpenid(Stringopenid){
this.openid=openid;
}
}
复制代码
后台业务逻辑涉及到的工具类及参数封装类
MessageUtil
packagecn.it.shop.util;
importjava.io.IOException;
importjava.io.Writer;
importjava.util.HashMap;
importjava.util.List;
importjavax.servlet.http.HttpServletRequest;
importorg.dom4j.Document;
importorg.dom4j.Element;
importorg.dom4j.io.SAXReader;
importcom.thoughtworks.xstream.XStream;
importcom.thoughtworks.xstream.core.util.QuickWriter;
importcom.thoughtworks.xstream.io.HierarchicalStreamWriter;
importcom.thoughtworks.xstream.io.xml.PrettyPrintWriter;
importcom.thoughtworks.xstream.io.xml.XppDriver;
publicclassMessageUtil{
publicstaticHashMapparseXML(HttpServletRequestrequest)throwsException,IOException{
HashMapmap=newHashMap();
//通过IO获得Document
SAXReaderreader=newSAXReader();
Documentdoc=reader.read(request.getInputStream());
//得到xml的根节点
Elementroot=doc.getRootElement();
recursiveParseXML(root,map);
returnmap;
}
privatestaticvoidrecursiveParseXML(Elementroot,HashMapmap){
//得到根节点的子节点列表
ListelementList=root.elements();
//判断有没有子元素列表
if(elementList.size()==0){
map.put(root.getName(),root.getTextTrim());
}
else{
//遍历
for(Elemente:elementList){
recursiveParseXML(e,map);
}
}
}
privatestaticXStreamxstream=newXStream(newXppDriver(){
publicHierarchicalStreamWritercreateWriter(Writerout){
returnnewPrettyPrintWriter(out){
//对所有xml节点都增加CDATA标记
booleancdata=true;
publicvoidstartNode(Stringname,Classclazz){
super.startNode(name,clazz);
}
protectedvoidwriteText(QuickWriterwriter,Stringtext){
if(cdata){
writer.write("writer.write(text);
writer.write("]]>");
}else{
writer.write(text);
}
}
};
}
});
publicstaticStringmessageToXML(PaymentPopaymentPo){
xstream.alias("xml",PaymentPo.class);
returnxstream.toXML(paymentPo);
}
}
PaymentPo//封装支付参数实体
packagecn.it.shop.util;
/**
*@author
*@version创建时间:2017年1月21日下午4:20:52
*类说明
*/
publicclassPaymentPo{
privateStringappid;//小程序ID
privateStringmch_id;//商户号
privateStringdevice_info;//设备号
privateStringnonce_str;//随机字符串
privateStringsign;//签名
privateStringbody;//商品描述
privateStringdetail;//商品详情
privateStringattach;//附加数据
privateStringout_trade_no;//商户订单号
privateStringfee_type;//货币类型
privateStringspbill_create_ip;//终端IP
privateStringtime_start;//交易起始时间
privateStringtime_expire;//交易结束时间
privateStringgoods_tag;//商品标记
privateStringtotal_fee;//总金额
privateStringnotify_url;//通知地址
privateStringtrade_type;//交易类型
privateStringlimit_pay;//指定支付方式
privateStringopenid;//用户标识
publicStringgetAppid(){
returnappid;
}
publicvoidsetAppid(Stringappid){
this.appid=appid;
}
publicStringgetMch_id(){
returnmch_id;
}
publicvoidsetMch_id(Stringmch_id){
this.mch_id=mch_id;
}
publicStringgetNonce_str(){
returnnonce_str;
}
publicvoidsetNonce_str(Stringnonce_str){
this.nonce_str=nonce_str;
}
publicStringgetSign(){
returnsign;
}
publicvoidsetSign(Stringsign){
this.sign=sign;
}
publicStringgetBody(){
returnbody;
}
publicvoidsetBody(Stringbody){
this.body=body;
}
publicStringgetOut_trade_no(){
returnout_trade_no;
}
publicvoidsetOut_trade_no(Stringout_trade_no){
this.out_trade_no=out_trade_no;
}
publicStringgetTotal_fee(){
returntotal_fee;
}
publicvoidsetTotal_fee(Stringtotal_fee){
this.total_fee=total_fee;
}
publicStringgetNotify_url(){
returnnotify_url;
}
publicvoidsetNotify_url(Stringnotify_url){
this.notify_url=notify_url;
}
publicStringgetTrade_type(){
returntrade_type;
}
publicvoidsetTrade_type(Stringtrade_type){
this.trade_type=trade_type;
}
publicStringgetOpenid(){
returnopenid;
}
publicvoidsetOpenid(Stringopenid){
this.openid=openid;
}
publicStringgetSpbill_create_ip(){
returnspbill_create_ip;
}
publicvoidsetSpbill_create_ip(Stringspbill_create_ip){
this.spbill_create_ip=spbill_create_ip;
}
publicStringgetDevice_info(){
returndevice_info;
}
publicvoidsetDevice_info(Stringdevice_info){
this.device_info=device_info;
}
publicStringgetDetail(){
returndetail;
}
publicvoidsetDetail(Stringdetail){
this.detail=detail;
}
publicStringgetAttach(){
returnattach;
}
publicvoidsetAttach(Stringattach){
this.attach=attach;
}
publicStringgetFee_type(){
returnfee_type;
}
publicvoidsetFee_type(Stringfee_type){
this.fee_type=fee_type;
}
publicStringgetTime_start(){
returntime_start;
}
publicvoidsetTime_start(Stringtime_start){
this.time_start=time_start;
}
publicStringgetTime_expire(){
returntime_expire;
}
publicvoidsetTime_expire(Stringtime_expire){
this.time_expire=time_expire;
}
publicStringgetGoods_tag(){
returngoods_tag;
}
publicvoidsetGoods_tag(Stringgoods_tag){
this.goods_tag=goods_tag;
}
publicStringgetLimit_pay(){
returnlimit_pay;
}
publicvoidsetLimit_pay(Stringlimit_pay){
this.limit_pay=limit_pay;
}
}
PayUtil
packagecn.it.shop.util;
importjava.io.BufferedReader;
importjava.io.InputStream;
importjava.io.InputStreamReader;
importjava.io.OutputStream;
importjava.io.UnsupportedEncodingException;
importjava.net.HttpURLConnection;
importjava.net.URL;
importjava.util.ArrayList;
importjava.util.Collections;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importorg.apache.commons.codec.digest.DigestUtils;
/**
*@author
*@version创建时间:2017年1月17日下午7:46:29类说明
*/
publicclassPayUtil{
/**
*签名字符串
*@paramtext需要签名的字符串
*@paramkey密钥
*@paraminput_charset编码格式
*@return签名结果
*/
publicstaticStringsign(Stringtext,Stringkey,Stringinput_charset){
text=text+key;
returnDigestUtils.md5Hex(getContentBytes(text,input_charset));
}
/**
*签名字符串
*@paramtext需要签名的字符串
*@paramsign签名结果
*@paramkey密钥
*@paraminput_charset编码格式
*@return签名结果
*/
publicstaticbooleanverify(Stringtext,Stringsign,Stringkey,Stringinput_charset){
text=text+key;
Stringmysign=DigestUtils.md5Hex(getContentBytes(text,input_charset));
if(mysign.equals(sign)){
returntrue;
}else{
returnfalse;
}
}
/**
*@paramcontent
*@paramcharset
*@return
*@throwsSignatureException
*@throwsUnsupportedEncodingException
*/
publicstaticbyte[]getContentBytes(Stringcontent,Stringcharset){
if(charset==null||"".equals(charset)){
returncontent.getBytes();
}
try{
returncontent.getBytes(charset);
}catch(UnsupportedEncodingExceptione){
thrownewRuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:"+charset);
}
}
/**
*生成6位或10位随机数paramcodeLength(多少位)
*@return
*/
publicstaticStringcreateCode(intcodeLength){
Stringcode="";
for(inti=0;i<codeLength;i++){
code+=(int)(Math.random()*9);
}
returncode;
}
privatestaticbooleanisValidChar(charch){
if((ch>='0'&&ch<='9')||(ch>='A'&&ch<='Z')||(ch>='a'&&ch<='z'))
returntrue;
if((ch>=0x4e00&&ch<=0x7fff)||(ch>=0x8000&&ch<=0x952f))
returntrue;//简体中文汉字编码
returnfalse;
}
/**
*除去数组中的空值和签名参数
*@paramsArray签名参数组
*@return去掉空值与签名参数后的新签名参数组
*/
publicstaticMapparaFilter(MapsArray){
Mapresult=newHashMap();
if(sArray==null||sArray.size()<=0){
returnresult;
}
for(Stringkey:sArray.keySet()){
Stringvalue=sArray.get(key);
if(value==null||value.equals("")||key.equalsIgnoreCase("sign")
||key.equalsIgnoreCase("sign_type")){
continue;
}
result.put(key,value);
}
returnresult;
}
/**
*把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*@paramparams需要排序并参与字符拼接的参数组
*@return拼接后字符串
*/
publicstaticStringcreateLinkString(Mapparams){
Listkeys=newArrayList(params.keySet());
Collections.sort(keys);
Stringprestr="";
for(inti=0;i<keys.size();i++){
Stringkey=keys.get(i);
Stringvalue=params.get(key);
if(i==keys.size()-1){//拼接时,不包括最后一个&字符
prestr=prestr+key+"="+value;
}else{
prestr=prestr+key+"="+value+"&";
}
}
returnprestr;
}
/**
*
*@paramrequestUrl请求地址
*@paramrequestMethod请求方法
*@paramoutputStr参数
*/
publicstaticStringhttpRequest(StringrequestUrl,StringrequestMethod,StringoutputStr){
//创建SSLContext
StringBufferbuffer=null;
try{
URLurl=newURL(requestUrl);
HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod(requestMethod);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
//往服务器端写内容
if(null!=outputStr){
OutputStreamos=conn.getOutputStream();
os.write(outputStr.getBytes("utf-8"));
os.close();
}
//读取服务器端返回的内容
InputStreamis=conn.getInputStream();
InputStreamReaderisr=newInputStreamReader(is,"utf-8");
BufferedReaderbr=newBufferedReader(isr);
buffer=newStringBuffer();
Stringline=null;
while((line=br.readLine())!=null){
buffer.append(line);
}
}catch(Exceptione){
e.printStackTrace();
}
returnbuffer.toString();
}
publicstaticStringurlEncodeUTF8(Stringsource){
Stringresult=source;
try{
result=java.net.URLEncoder.encode(source,"UTF-8");
}catch(UnsupportedEncodingExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
returnresult;
}
}
UUIDHexGenerator
packagecn.it.shop.util;
importjava.net.InetAddress;
/**
*@author
*@version创建时间:2017年1月17日下午7:45:06类说明
*/
publicclassUUIDHexGenerator{
privatestaticStringsep="";
privatestaticfinalintIP;
privatestaticshortcounter=(short)0;
privatestaticfinalintJVM=(int)(System.currentTimeMillis()>>>;
privatestaticUUIDHexGeneratoruuidgen=newUUIDHexGenerator();
static{
intipadd;
try{
ipadd=toInt(InetAddress.getLocalHost().getAddress());
}catch(Exceptione){
ipadd=0;
}
IP=ipadd;
}
publicstaticUUIDHexGeneratorgetInstance(){
returnuuidgen;
}
publicstaticinttoInt(byte[]bytes){
intresult=0;
for(inti=0;i<4;i++){
result=(result<<-Byte.MIN_VALUE+(int)bytes;
}
returnresult;
}
protectedstaticStringformat(intintval){
Stringformatted=Integer.toHexString(intval);
StringBufferbuf=newStringBuffer("00000000");
buf.replace(8-formatted.length(),8,formatted);
returnbuf.toString();
}
protectedstaticStringformat(shortshortval){
Stringformatted=Integer.toHexString(shortval);
StringBufferbuf=newStringBuffer("0000");
buf.replace(4-formatted.length(),4,formatted);
returnbuf.toString();
}
protectedstaticintgetJVM(){
returnJVM;
}
protectedsynchronizedstaticshortgetCount(){
if(counter<0){
counter=0;
}
returncounter++;
}
protectedstaticintgetIP(){
returnIP;
}
protectedstaticshortgetHiTime(){
return(short)(System.currentTimeMillis()>>>32);
}
protectedstaticintgetLoTime(){
return(int)System.currentTimeMillis();
}
publicstaticStringgenerate(){
returnnewStringBuffer(36).append(format(getIP())).append(sep).append(format(getJVM())).append(sep)
.append(format(getHiTime())).append(sep).append(format(getLoTime())).append(sep)
.append(format(getCount())).toString();
}
/**
*@paramargs
*/
publicstaticvoidmain(String[]args){
Stringid="";
UUIDHexGeneratoruuid=UUIDHexGenerator.getInstance();
/*
for(inti=0;i<100;i++){
id=uuid.generate();
}*/
id=uuid.generate();
System.out.println(id);
}
}
复制代码
转自解放号社区:http://bbs.jointforce.com/topic/25765