小程序支付说明
此案例用作小程序缴费的情况。过程为:先触发微信小程序支付后台得到预付款ID(prepare_id),然后小程序前台会自动弹出一个对应金额的支付窗口,然后用户输入正确密码,支付成功,触发用户的回调函数做业务操作。
更多分布式微服务课程关注:www.majiaxueyuan.com
得到微信的code。小程序和公众号都可以直接得到
AccessToken获取方法(顺便写的)
Body<String> getAccessToken(String code) {
CloseableHttpClient httpClient = HttpClients.createDefault();
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + WxPayConfig.APP_ID + "&secret="
+ WxPayConfig.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
// 创建一个GET对象
try {
HttpGet get = new HttpGet(url);
// 执行请求
CloseableHttpResponse response = httpClient.execute(get);
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity, "utf-8");
System.out.println(string);
return new Body<String>().setSuccess(string);
} catch (Exception e) {
return new Body<String>().setError("登录异常");
}
}
微信支付配置文件
public class WxPayConfig {
// 小程序appid
public static final String APP_ID = "";
// APP——Secret
public static final String APP_SECRET = "";
// 微信支付的商户号
public static final String MCH_ID = "";
// 微信支付的商户秘钥
public static final String KEY = "";
// 支付成功后的服务器回调url
public static final String notify_url = "";
// 签名方式,固定值
public static final String SIGNTYPE = "MD5";
// 交易类型,小程序支付的固定值为JSAPI
public static final String TRADETYPE = "JSAPI";
// 微信统一下单接口地址
public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 微信直接转账到个人请求
public static final String transfor_url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
public static final String STATIC_NUM = "a0A0b1B2c1C3d2D1e3E2f4F3g5G7h4H6i5Ij4J9k5K6l6Lm7M7n8N8o9Op0PqQrRsStTuUv9VwWxXy8YzZ";
}
预付款ID
微信支付分为两步,第一步可以理解为获得一个预付款ID
/**
* 微信支付获取预付款信息返回给小程序唤起支付界面
* @param unique:系统订单号
* @param openId:用户唯一OpenId
* @param totleMoney:付款金额,字符串(单位:分)
* @param ipAddr:IP地址
* @return
*/
public Body<String> wxPay(String unique, String openId, String totleMoney, String ipAddr) {
// 付款金额
try {
// 生成的随机字符串
String nonce_str = getRandomStringByLength(32);
// 获取客户端的ip地址
String spbill_create_ip = ipAddr;
// 商品名称
String body = "码家学院永久会员会员充值";
// 组装参数,用户生成统一下单接口的签名
Map<String, String> packageParams = new HashMap<String, String>();
packageParams.put("appid", WxPayConfig.APP_ID);
packageParams.put("body", body);
packageParams.put("mch_id", WxPayConfig.MCH_ID);
packageParams.put("nonce_str", nonce_str);
packageParams.put("notify_url", WxPayConfig.notify_url);// 支付成功后的回调地址
packageParams.put("openid", openId);
packageParams.put("out_trade_no", unique);// 商户订单号
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("total_fee", totleMoney); // 支付金额,这边需要转成字符串类型,否则后面的签名会失败
packageParams.put("trade_type", WxPayConfig.TRADETYPE);// 支付方式
String prestr = PayUtil.createLinkString(packageParams); //
// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
String mysign = PayUtil.sign(prestr, WxPayConfig.KEY, "utf-8").toUpperCase();
// 拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
String xml = "<xml>" + "<appid>" + WxPayConfig.APP_ID + "</appid>" + "<body><![CDATA[" + body + "]]></body>"
+ "<mch_id>" + WxPayConfig.MCH_ID + "</mch_id>" + "<nonce_str>" + nonce_str + "</nonce_str>"
+ "<notify_url>" + WxPayConfig.notify_url + "</notify_url>" + "<openid>" + openId + "</openid>"
+ "<out_trade_no>" + unique + "</out_trade_no>" + "<spbill_create_ip>" + spbill_create_ip
+ "</spbill_create_ip>" + "<total_fee>" + totleMoney + "</total_fee>" + "<trade_type>"
+ WxPayConfig.TRADETYPE + "</trade_type>" + "<sign>" + mysign + "</sign>" + "</xml>";
System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);
// 调用统一下单接口,并接受返回的结果
String result = PayUtil.httpRequest(WxPayConfig.pay_url, "POST", xml);
System.out.println("调试模式_统一下单接口 返回XML数据:" + result);
// 将解析结果存储在HashMap中
Map map = PayUtil.doXMLParse(result);
// 返回状态码
String return_code = (String) map.get("return_code");
// 返回给小程序端需要的参数
Map<String, Object> response = new HashMap<String, Object>();
if (return_code == "SUCCESS" || return_code.equals(return_code)) {
// 返回的预付单信息
String prepay_id = (String) map.get("prepay_id");
response.put("nonceStr", nonce_str);
response.put("package", "prepay_id=" + prepay_id);
Long timeStamp = System.currentTimeMillis() / 1000;
response.put("timeStamp", timeStamp + "");// 这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
// 拼接签名需要的参数
String stringSignTemp = "appId=" + WxPayConfig.APP_ID + "&nonceStr=" + nonce_str + "&package=prepay_id="
+ prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String paySign = PayUtil.sign(stringSignTemp, WxPayConfig.KEY, "utf-8").toUpperCase();
response.put("paySign", paySign);
}
response.put("appid", WxPayConfig.APP_ID);
String json = JSON.toJSONString(response);
return new Body<String>().setSuccess(json);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
随机字符串生成方法
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
异步回调函数
(用户处理我们自己系统中的逻辑)
public void wxNotify(HttpServletRequest request, HttpServletResponse response) {
try {
BufferedReader br = new BufferedReader(
new InputStreamReader((ServletInputStream) request.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
// sb为微信返回的xml
String notityXml = sb.toString();
String resXml = "";
System.out.println("接收到的报文:" + notityXml);
Map map = PayUtil.doXMLParse(notityXml);
String returnCode = (String) map.get("return_code");
if ("SUCCESS".equals(returnCode)) {
// 支付成功,需要根据订单号来判断此订单号在系统中是否已经被处理过了
// 这就是所说的幂等性的处理,避免我们的订单被交易多次
// 处理完成过后告诉微信处理成功,避免微信触发重试机制
map.get("result_code");
map.get("transaction_id");
// 告诉微信处理完成
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
// 告诉微信处理失败
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
System.out.println("微信支付回调数据结束");
BufferedOutputStream out;
out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
到这里小程序当中的微信支付就完成了,也是很简答的