微信公众号支付(二)实现统一下单接口
程序员文章站
2024-03-06 15:42:44
已经获取到了用户的openid
这篇主要是调用微信公众支付的统一下单api
api地址:
看文档,主要流程就是把20个左右的参数封装为xml格式发送到微信给的接口地址...
已经获取到了用户的openid
这篇主要是调用微信公众支付的统一下单api
api地址:
看文档,主要流程就是把20个左右的参数封装为xml格式发送到微信给的接口地址,然后就可以获取到返回的内容了,如果成功里面就有支付所需要的预支付id
请求参数就不解释了。
其中,随机字符串:我用的是uuid去中划线
public static string create_nonce_str() { return uuid.randomuuid().tostring().replace("-",""); }
商户订单号:每个订单号只能使用一次,所以用的是系统的订单号加的时间戳。
总金额:不能为
通知地址:微信支付成功或失败回调给系统的地址
签名:
import java.io.serializable; public class payinfo implements serializable{ private static final long serialversionuid = l; private string appid; private string mch_id; private string device_info; private string nonce_str; private string sign; private string body; private string attach; private string out_trade_no; private int total_fee; private string spbill_create_ip; private string notify_url; private string trade_type; private string openid; //下面是get,set方法 } /** * 创建统一下单的xml的java对象 * @param bizorder 系统中的业务单号 * @param ip 用户的ip地址 * @param openid 用户的openid * @return */ public payinfo createpayinfo(bizorder bizorder,string ip,string openid) { payinfo payinfo = new payinfo(); payinfo.setappid(constants.appid); payinfo.setdevice_info("web"); payinfo.setmch_id(constants.mch_id); payinfo.setnonce_str(commonutil.create_nonce_str().replace("-", "")); payinfo.setbody("这里是某某白米饭的body"); payinfo.setattach(bizorder.getid()); payinfo.setout_trade_no(bizorder.getordercode().concat("a").concat(dateformatutils.format(new date(), "mmddhhmmss"))); payinfo.settotal_fee((int)bizorder.getfeeamount()); payinfo.setspbill_create_ip(ip); payinfo.setnotify_url(constants.notify_url); payinfo.settrade_type("jsapi"); payinfo.setopenid(openid); return payinfo; }
获取签名:
/** * 获取签名 * @param payinfo * @return * @throws exception */ public string getsign(payinfo payinfo) throws exception { string signtemp = "appid="+payinfo.getappid() +"&attach="+payinfo.getattach() +"&body="+payinfo.getbody() +"&device_info="+payinfo.getdevice_info() +"&mch_id="+payinfo.getmch_id() +"&nonce_str="+payinfo.getnonce_str() +"¬ify_url="+payinfo.getnotify_url() +"&openid="+payinfo.getopenid() +"&out_trade_no="+payinfo.getout_trade_no() +"&spbill_create_ip="+payinfo.getspbill_create_ip() +"&total_fee="+payinfo.gettotal_fee() +"&trade_type="+payinfo.gettrade_type() +"&key="+constants.key; //这个key注意 messagedigest md = messagedigest.getinstance("md"); md.reset(); md.update(signtemp.getbytes("utf-")); string sign = commonutil.bytetostr(md.digest()).touppercase(); return sign; }
注意:上面的constants.key取值在商户号api安全的api密钥中。
一些工具方法:获取ip地址,将字节数组转换为十六进制字符串,将字节转换为十六进制字符串
/** * 将字节数组转换为十六进制字符串 * * @param bytearray * @return */ public static string bytetostr(byte[] bytearray) { string strdigest = ""; for (int i = ; i < bytearray.length; i++) { strdigest += bytetohexstr(bytearray[i]); } return strdigest; } /** * 将字节转换为十六进制字符串 * * @param btyes * @return */ public static string bytetohexstr(byte bytes) { char[] digit = { '', '', '', '', '', '', '', '', '', '', 'a', 'b', 'c', 'd', 'e', 'f' }; char[] temparr = new char[]; temparr[] = digit[(bytes >>> ) & xf]; temparr[] = digit[bytes & xf]; string s = new string(temparr); return s; } /** * 获取ip地址 * @param request * @return */ public static string getipaddr(httpservletrequest request) { inetaddress addr = null; try { addr = inetaddress.getlocalhost(); } catch (unknownhostexception e) { return request.getremoteaddr(); } byte[] ipaddr = addr.getaddress(); string ipaddrstr = ""; for (int i = ; i < ipaddr.length; i++) { if (i > ) { ipaddrstr += "."; } ipaddrstr += ipaddr[i] & xff; } return ipaddrstr; }
这样就获取了签名,把签名与payinfo中的其他数据转成xml格式,当做参数传递给统一下单地址。
payinfo pi = pu.createpayinfo(bo,"...",""); string sign = pu.getsign(pi); pi.setsign(sign);
对象转xml
/** * 扩展xstream使其支持cdata */ private static xstream xstream = new xstream(new xppdriver() { public hierarchicalstreamwriter createwriter(writer out) { return new prettyprintwriter(out) { //增加cdata标记 boolean cdata = true; @suppresswarnings("rawtypes") public void startnode(string name, class clazz) { super.startnode(name, clazz); } protected void writetext(quickwriter writer, string text) { if (cdata) { writer.write("<![cdata["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); public static string payinfotoxml(payinfo pi) { xstream.alias("xml", pi.getclass()); return xstream.toxml(pi); }
xml转map
@suppresswarnings("unchecked") public static map<string, string> parsexml(string xml) throws exception { map<string, string> map = new hashmap<string, string>(); document document = documenthelper.parsetext(xml); element root = document.getrootelement(); list<element> elementlist = root.elements(); for (element e : elementlist) map.put(e.getname(), e.gettext()); return map; }
下面就是调用统一下单的url了
log.info(messageutil.payinfotoxml(pi).replace("__", "_")); map<string, string> map = commonutil.httpsrequesttoxml("https://api.mch.weixin.qq.com/pay/unifiedorder", "post", messageutil.payinfotoxml(pi).replace("__", "_").replace("<![cdata[", "").replace("]]>", "")); log.info(map); public static map<string, string> httpsrequesttoxml(string requesturl, string requestmethod, string outputstr) { map<string, string> result = new hashmap<>(); try { stringbuffer buffer = httpsrequest(requesturl, requestmethod, outputstr); result = messageutil.parsexml(buffer.tostring()); } catch (connectexception ce) { log.error("连接超时:"+ce.getmessage()); } catch (exception e) { log.error("https请求异常:"+ece.getmessage()); } return result; }
httpsrequest()这个方法在第一篇中
上面获取到的map如果成功的话,里面就会有
string return_code = map.get("return_code"); if(stringutils.isnotblank(return_code) && return_code.equals("success")){ string return_msg = map.get("return_msg"); if(stringutils.isnotblank(return_msg) && !return_msg.equals("ok")) { return "统一下单错误!"; } }else{ return "统一下单错误!"; } string prepay_id = map.get("prepay_id");
这个prepay_id就是预支付的id。后面支付需要它。