微信小程序支付模式(一)
微信小程序之路是将我这一年以来做微信小程序开发遇到的一些较为难的知识点记录下来。
微信小程序支付模式
微信支付模式(一)
微信小程序代码:
app.wxRequest("GET","/wx/wxPay?openid="+openid+"&fee="+fee+"&orderId="+orderId,null,suc=>{
wx.requestPayment({
'timeStamp': suc.data.timeStamp,
'nonceStr': suc.data.nonce_str,
'package': suc.data.packageValue,
'signType': 'MD5',
'paySign': suc.data.sign,
'success': function (suc) {
console.log('支付完成');
if (res.errMsg == 'requestPayment:ok') {
wx.showModal({
title: '提示',
content: '支付成功!'
});
},
'fail': function (suc) {
wx.showToast({
title: '支付失败!'
})
},
'complete': function (res) {
}
})
})
wx.requestPayment 获取微信支付结果
参数 | 解析 |
---|---|
timeStamp | 时间戳 |
nonceStr | 随机字符串 |
package | 统一下单接口返回的 prepay_id 参数值 |
signType | 签名算法 |
paySign | 支付签名 |
success | 接口成功回调 |
fail | 接口失败回调 |
complete | 接口完成回调(成功,失败都执行) |
java后台
random:获取随机数类
import java.util.Random;
/**
* @author orange
* 随机数生成算法
*/
public class StringRandom {
public static String getRandomString(int length){
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random=new Random();
StringBuffer sb=new StringBuffer();
for(int i=0;i<length;i++){
int number=random.nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
}
}
微信支付配置文件
public static final String mch_id = "1562****21";
public static final String AES = "AES";
public static final String AES_CBC_PADDING = "AES/CBC/PKCS7Padding";
public static String trade_type = "JSAPI";
public static String signType = "MD5";
RequestHandler:微信支付服务器签名支付请求请求类
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
'微信支付服务器签名支付请求请求类
'============================================================================
'api说明:
'init(app_id, app_secret, partner_key, app_key);
'初始化函数,默认给一些参数赋值,如cmdno,date等。
'setKey(key_)'设置商户密钥
'getLasterrCode(),获取最后错误号
'GetToken();获取Token
'getTokenReal();Token过期后实时获取Token
'createMd5Sign(signParams);生成Md5签名
'genPackage(packageParams);获取package包
'createSHA1Sign(signParams);创建签名SHA1
'sendPrepay(packageParams);提交预支付
'getDebugInfo(),获取debug信息
'============================================================================
'*/
public class RequestHandler {
/** Token获取网关地址地址 */
private String tokenUrl;
/** 预支付网关url地址 */
private String gateUrl;
/** 查询支付通知网关URL */
private String notifyUrl;
/** 商户参数 */
private String appid;
private String appkey;
private String partnerkey;
private String appsecret;
private String key;
/** 请求的参数 */
private SortedMap parameters;
/** Token */
private String Token;
private String charset;
/** debug信息 */
private String debugInfo;
private String last_errcode;
private HttpServletRequest request;
private HttpServletResponse response;
/**
* 初始构造函数。
*
* @return
*/
public RequestHandler(HttpServletRequest request,
HttpServletResponse response) {
this.last_errcode = "0";
this.request = request;
this.response = response;
//this.charset = "GBK";
this.charset = "UTF-8";
this.parameters = new TreeMap();
// 验证notify支付订单网关
notifyUrl = "https://gw.tenpay.com/gateway/simpleverifynotifyid.xml";
}
/**
* 初始化函数。
*/
public void init(String app_id, String app_secret, String partner_key) {
this.last_errcode = "0";
this.Token = "token_";
this.debugInfo = "";
this.appid = app_id;
this.partnerkey = partner_key;
this.appsecret = app_secret;
this.key = partner_key;
}
public void init() {
}
/**
* 获取最后错误号
*/
public String getLasterrCode() {
return last_errcode;
}
/**
*获取入口地址,不包含参数值
*/
public String getGateUrl() {
return gateUrl;
}
/**
* 获取参数值
*
* @param parameter
* 参数名称
* @return String
*/
public String getParameter(String parameter) {
String s = (String) this.parameters.get(parameter);
return (null == s) ? "" : s;
}
//设置密钥
public void setKey(String key) {
this.partnerkey = key;
}
//设置微信密钥
public void setAppKey(String key){
this.appkey = key;
}
// 特殊字符处理
public String UrlEncode(String src) throws UnsupportedEncodingException {
return URLEncoder.encode(src, this.charset).replace("+", "%20");
}
// 获取package的签名包
public String genPackage(SortedMap<String, String> packageParams)
throws UnsupportedEncodingException {
String sign = createSign(packageParams);
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
sb.append(k + "=" + UrlEncode(v) + "&");
}
// 去掉最后一个&
String packageValue = sb.append("sign=" + sign).toString();
// System.out.println("UrlEncode后 packageValue=" + packageValue);
return packageValue;
}
/**
* 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*/
public String createSign(SortedMap<String, String> packageParams) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k)
&& !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + this.getKey());
System.out.println("md5 sb:" + sb);
String sign = MD5Util.MD5Encode(sb.toString(), this.charset)
.toUpperCase();
System.out.println("packge签名:" + sign);
return sign;
}
/**
* 创建package签名
*/
public boolean createMd5Sign(String signParams) {
StringBuffer sb = new StringBuffer();
Set es = this.parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
// 算出摘要
String enc = TenpayUtil.getCharacterEncoding(this.request,
this.response);
String sign = MD5Util.MD5Encode(sb.toString(), enc).toLowerCase();
String tenpaySign = this.getParameter("sign").toLowerCase();
// debug信息
this.setDebugInfo(sb.toString() + " => sign:" + sign + " tenpaySign:"
+ tenpaySign);
return tenpaySign.equals(sign);
}
//输出XML
public String parseXML() {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = this.parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if(null != v && !"".equals(v) && !"appkey".equals(k)) {
sb.append("<" + k +">" + getParameter(k) + "</" + k + ">\n");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 设置debug信息
*/
protected void setDebugInfo(String debugInfo) {
this.debugInfo = debugInfo;
}
public void setPartnerkey(String partnerkey) {
this.partnerkey = partnerkey;
}
public String getDebugInfo() {
return debugInfo;
}
public String getKey() {
return partnerkey;
}
}
WeixinPayUtil:支付工具类
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
/**
* 微信支付工具类
* @author liyizhong
* @since 2015-09-01
*
*/
public class WeixinPayUtil {
public static final String SUBMIT_URL ="https://api.mch.weixin.qq.com/pay/unifiedorder";
public static DefaultHttpClient httpclient;
static {
httpclient = new DefaultHttpClient(new ThreadSafeClientConnManager());
httpclient = (DefaultHttpClient) HttpClientConnectionManager
.getSSLInstance(httpclient);
}
public static String getPayNo(String url, String xmlParam) {
DefaultHttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager());
client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,
true);
HttpPost httpost = HttpClientConnectionManager.getPostMethod(url);
String prepay_id = "";
try {
httpost.setEntity(new StringEntity(xmlParam, "UTF-8"));
HttpResponse response = httpclient.execute(httpost);
String jsonStr = EntityUtils
.toString(response.getEntity(), "UTF-8");
Map<String, Object> dataMap = new HashMap<String, Object>();
System.out.println("jsonStr============================"+jsonStr);
if (jsonStr.indexOf("FAIL") != -1) {
return prepay_id;
}
Map map = doXMLParse(jsonStr);
System.out.println("第一次签名数据:"+map);
String return_code = (String) map.get("return_code");
String code_url = (String) map.get("code_url");
System.out.println("code_url:"+code_url);
prepay_id = (String) map.get("prepay_id");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return prepay_id;
}
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws Exception {
if (null == strxml || "".equals(strxml)) {
return null;
}
//System.out.println("strxml="+strxml);
Map m = new HashMap();
InputStream in = String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
builder.setFeature(FEATURE, true);
FEATURE = "http://xml.org/sax/features/external-general-entities";
builder.setFeature(FEATURE, false);
FEATURE = "http://xml.org/sax/features/external-parameter-entities";
builder.setFeature(FEATURE, false);
FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
builder.setFeature(FEATURE, false);
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
// 关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param
* @return String
*//**//**//**/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
public static InputStream String2Inputstream(String str) {
return new ByteArrayInputStream(str.getBytes());
}
/**
* 获取统一订单提交返回字符串值
* @param url
* @param xmlParam
* @return
*/
public static String getTradeOrder(String url, String xmlParam) {
DefaultHttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager());
client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,
true);
HttpPost httpost = HttpClientConnectionManager.getPostMethod(url);
String jsonStr = "";
try {
httpost.setEntity(new StringEntity(xmlParam, "UTF-8"));
HttpResponse response = httpclient.execute(httpost);
jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return jsonStr;
}
public static String getSign(Map<String,String> paramMap,String key){
List list = new ArrayList(paramMap.keySet());
Object[] ary = list.toArray();
Arrays.sort(ary);
list = Arrays.asList(ary);
String str = "";
for(int i=0;i<list.size();i++){
str+=list.get(i)+"="+paramMap.get(list.get(i)+"")+"&";
}
str+="key="+key;
str = MD5Util.MD5Encode(str, "UTF-8").toUpperCase();
return str;
}
/**
* 处理xml请求信息
* @param request
* @return
*/
public static String getXmlRequest(HttpServletRequest request){
java.io.BufferedReader bis = null;
String result = "";
try{
bis = new java.io.BufferedReader(new java.io.InputStreamReader(request.getInputStream()));
String line = null;
while((line = bis.readLine()) != null){
result += line;
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(bis != null){
try{
bis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
return result;
}
}
MD5Util
import java.security.MessageDigest;
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
}else {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
}
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
controller
@GetMapping("/wxPay")
public Map<String, String> wxPay(String openid, String fee,String orderId,HttpServletRequest request, HttpServletResponse response){
String notify_url = SystemConfig.url+"/wx/notify";//回调接口
SortedMap<String, String> packageParams = new TreeMap<String, String>();//用于接收数据
//以下是产生预付单的必要字段
String nonce_str = StringRandom.getRandomString(32);
// String out_trade_no = StringRandom.getRandomString(18);//商户系统内部订单号.32字符以内
String body = "商品总价";//商品描述,如:腾讯充值中心-QQ会员充值
String total_fee =fee;//支付金额
String spbill_create_ip = request.getRemoteAddr();//终端IP
String trade_type = WxPayConfig.trade_type;//支付类型
//设置参数
packageParams.put("appid", appid);
packageParams.put("mch_id", WxPayConfig.mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("out_trade_no", orderId);
packageParams.put("total_fee", total_fee);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);
RequestHandler reqHandler = new RequestHandler(request, response);//字符编码
reqHandler.init(appid, WxPayConfig.appsecret, WxPayConfig.partnerkey);//初始化函数
String sign = reqHandler.createSign(packageParams);//创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名
//请求参数
String xml = "<xml>" + "<appid>" + appid + "</appid>" + "<mch_id>" + WxPayConfig.partner
+ "</mch_id>" + "<nonce_str>" + nonce_str + "</nonce_str>" + "<sign>" + sign + "</sign>"
+ "<body><![CDATA[" + body + "]]></body>" + "<out_trade_no>" + orderId + "</out_trade_no>"
+ "<total_fee>" + total_fee + "" + "</total_fee>" + "<spbill_create_ip>" + request.getRemoteAddr()
+ "</spbill_create_ip>" + "<notify_url>" + notify_url + "</notify_url>" + "<trade_type>"
+ WxPayConfig.trade_type + "</trade_type>" + "<openid>" + openid + "</openid>" + "</xml>";
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//生成预付单接口
String prepay_id = "";//预付单id
try {
prepay_id = WeixinPayUtil.getPayNo(createOrderURL, xml);//获取预付id
// System.out.println(prepay_id);
if (prepay_id.equals("")) {
request.setAttribute("ErrorMsg", "统一支付接口获取预支付订单出错");
response.sendRedirect("toError");
}
} catch (Exception e1) {
e1.printStackTrace();
}
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String timestamp = Sha1Util.getTimeStamp();
String packages = "prepay_id=" + prepay_id;
finalpackage.put("appId", appid);
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonce_str);
finalpackage.put("package", packages);
finalpackage.put("signType", WxPayConfig.signType);
String finalsign = reqHandler.createSign(finalpackage);
Map<String,String> map=new HashMap<>();
map.put("prepay_id",prepay_id);
map.put("appid", appid);
map.put("timeStamp",timestamp);
map.put("nonce_str",nonce_str);
map.put("packageValue",packages);
map.put("payPrice",total_fee);
map.put("sign",finalsign);
return map;
}
微信小程序支付回调
/**
* 支付完成后,回调查询缴费(防止模拟缴费),
*/
@RequestMapping(value="/notify",produces = "application/xml;charset=utf-8")
@ResponseBody
public String notify(HttpServletRequest request, HttpServletResponse response)throws Exception {
String xml_review_result = WeixinPayUtil.getXmlRequest(request);
System.out.println("微信支付结果:" + xml_review_result);
Map resultMap = null;
resultMap = WeixinPayUtil.doXMLParse(xml_review_result);
String resXml = orderService.updatePayOrder(resultMap);
return resXml;
}
这就是微信支付模式一全部代码了,做了好久了,整理的可能有些遗漏。