您的位置:首页 > 移动开发 > 微信开发

Java 微信支付之APP支付服务端 (一)

2017-11-06 18:13 543 查看
Java 微信支付之APP支付服务端 (一)

如图所示,这是服务端要集成的所有微信接口。至于在开放平台申请就不做赘述了。主要流程,1.下单,2.异步通知,3.查询。



一、微信统一下单请求交易
/**
* 微信统一下单请求交易
*
* @param userId
* @param amount
* @return
*/
@RequestMapping(value=Route.Payment.WEIXIN_PAY,method=RequestMethod.POST)
@ResponseBody
public Response pay(@PathVariable("userId") int userId,@PathVariable("amount") String amount) {
log.info("InfoMsg:--- 微信统一下单请求交易开始");
Response resp = this.getReponse();
String message = "";
try {
ThirdPayBean bean = thirdPayService.findByPayId(19);
String mch_id = bean.getMer_no();   //商户号
String notify_url = bean.getReturn_url(); //通知地址
PlatformMutualityManagent pmm = platformMutualityManagentService.findOne(5);
String appid = pmm.getClient_id();        //应用ID
String App_Secret = pmm.getClient_secret();
String nonce_str = WXPayUtil.generateNonceStr();            //随机字符串
String sign = "";                   //签名
String sign_type = "MD5";                       //签名类型
String body = "循心";                     //商品描述 ps:腾讯充值中心-QQ会员充值
String out_trade_no = OrderGeneratedUtils.getOrderNo();         //商户订单号
int total_fee = WXRandomNumberUtil.wx_format_PayAmount(amount); //交易金额
String spbill_create_ip = InetAddress.getLocalHost().getHostAddress(); //终端IP
String trade_type = "APP";          //交易类型
String attach = userId + "|" + bean.getBank_id();      //附加数据 ps:用户|支付方式

StringBuffer sb = new StringBuffer();
sb.append("appid=").append(appid).append("&");
sb.append("attach=").append(attach).append("&");
sb.append("body=").append(body).append("&");
sb.append("mch_id=").append(mch_id).append("&");
sb.append("notify_url=").append(notify_url).append("&");
sb.append("nonce_str=").append(nonce_str).append("&");
sb.append("out_trade_no=").append(out_trade_no).append("&");
sb.append("sign_type=").append(sign_type).append("&");
sb.append("spbill_create_ip=").append(spbill_create_ip).append("&");
sb.append("total_fee=").append(total_fee).append("&");
sb.append("trade_type=").append(trade_type).append("&");
String params = sb.toString();
//需要签名的数据
String stringSignTemp = params + "&key=" + App_Secret;
//MD5签名方式
sign = WXPayUtil.MD5(stringSignTemp).toUpperCase();
Map map = new HashMap<>();
map.put("appid", appid);
map.put("attach", attach);
map.put("body", body);
map.put("mch_id", mch_id);
map.put("notify_url", notify_url);
map.put("nonce_str", nonce_str);
map.put("out_trade_no", out_trade_no);
map.put("sign", sign);
map.put("sign_type", sign_type);
map.put("spbill_create_ip", spbill_create_ip);
map.put("total_fee", total_fee);
map.put("trade_type", trade_type);

List theaderList = new ArrayList<>();
theaderList.add(new UHeader("Content-Type", "application/x-www-form-urlencoded"));
//Httpclient发送请求
String postResponse = MaryunHttpUtils.getPostResponse(weixin_pay_Url, map, theaderList);
//解析返回的XML数据
Map response = WXPayUtil.xmlToMap(postResponse);
if(!response.isEmpty() && response.get("return_code").equals("SUCCESS")){
if(response.get("result_code").equals("SUCCESS")) {
boolean result = rechargeRecordService.generatedBills(response,attach);
if(result) {
log.info("InfoMsg:--- 微信统一下单请求交易成功");
}
}else {
message = response.get("err_code_des");
log.error("errorMsg:--- 微信统一下单请求交易解析失败" + message);
}
}else {
log.error("errorMsg:--- 微信统一下单请求交易解析失败" + message);
}
log.info("InfoMsg:--- 微信统一下单请求交易结束");
return resp.success();
} catch (Exception e) {
log.error("errorMsg:--- 微信统一下单请求交易失败" + e.getMessage());
return resp.failure(e.getMessage());
}

}


二、微信异步通知

/**
* 微信支付通知地址
*
* @param request
* @return
*/
@RequestMapping(value=Route.Payment.WEIXIN_PAY_NOTIFY,method=RequestMethod.POST)
@ResponseBody
public Response weixin_pay_notify(HttpServletRequest request) {
log.info("infoMsg:--- 微信异步通知开始");
Response resp = this.getReponse();
BufferedReader reader = null;
String wx_map = "";
try {
PlatformMutualityManagent pmm = platformMutualityManagentService.findOne(5);
Assert.notNull(pmm);
String app_key = pmm.getClient_id();
reader = request.getReader();
String line = "";
String xmlString = null;
StringBuffer inputString = new StringBuffer();
while ((line = reader.readLine()) != null) {
inputString.append(line);
}
xmlString = inputString.toString();
request.getReader().close();
if(!StringUtils.isBlank(xmlString)) {

4000
Map return_map = WXPayUtil.xmlToMap(xmlString);
//验签
if(WXPayUtil.isSignatureValid(xmlString, app_key)) {
if(return_map.get("return_map").equals("SUCCESS")) {
//TODO 账变,修改状态,到账提醒
Double amount = Double.parseDouble(return_map.get("total_fee"));
String passbackParams = return_map.get("total_fee");
String order_no = return_map.get("out_trade_no");
boolean result = rechargeRecordService.updateBill(amount,passbackParams,order_no);
if(result) {
Map map = new HashMap<>();
map.put("return_map", "SUCCESS");
map.put("return_msg", "OK");
wx_map = WXPayUtil.mapToXml(map);
}
}
}
}
log.info("infoMsg:--- 微信异步通知失败");
return resp.success(wx_map);
} catch (Exception e) {
log.error("errorMsg:--- 微信通知失败" + e.getMessage());
return resp.failure(e.getMessage());
}
}


三、微信支付订单查询

/**
* 微信支付订单查询
*
* @param transaction_id 微信订单号
* @param out_trade_no  平台订单号
* @return
*/
@RequestMapping(value=Route.Payment.WEIXIN_PAY_QUERY,method=RequestMethod.POST)
@ResponseBody
public Response weixin_pay_query(@PathVariable("transaction_id") String transaction_id,@PathVariable("out_trade_no") String out_trade_no) {
log.info("infoMsg:--- 微信支付订单查询开始");
Response resp = this.getReponse();
String sign = "";
String message = "";
Map return_map = null;
try {
ThirdPayBean bean = thirdPayService.findByPayId(19);
Assert.notNull(bean);
String mch_id = bean.getMer_no();   //商户号
PlatformMutualityManagent pmm = platformMutualityManagentService.findOne(5);
Assert.notNull(pmm);
String appid = pmm.getClient_id();        //应用ID
String App_Secret = pmm.getClient_secret();
String nonce_str = WXPayUtil.generateNonceStr();            //随机字符串

StringBuffer sb = new StringBuffer();
sb.append("appid=").append(appid).append("&");
sb.append("nonce_str=").append(nonce_str).append("&");
sb.append("out_trade_no=").append(out_trade_no).append("&");
String params = sb.toString();
//需要签名的数据
String stringSignTemp = params + "&key=" + App_Secret;
//MD5签名方式
sign = WXPayUtil.MD5(stringSignTemp).toUpperCase();
Map req_map = new HashMap<>();
req_map.put("appid", appid);
req_map.put("mch_id", mch_id);
req_map.put("transaction_id", transaction_id);
req_map.put("out_trade_no", out_trade_no);
req_map.put("nonce_str", nonce_str);
req_map.put("sign", sign);
List headerList = new ArrayList<>();
headerList.add(new UHeader("Content-Type", "application/x-www-form-urlencoded"));
String postResponse = MaryunHttpUtils.getPostResponse(weixin_query_Url, req_map, headerList);
if(StringUtils.trim(postResponse).equals("")) {
return_map = WXPayUtil.xmlToMap(postResponse);
if(!return_map.isEmpty()) {
String return_code = return_map.get("return_code");
if(return_code.equals("SUCCESS")) {
String result_code = return_map.get("return_code");
if(result_code.equals("SUCCESS")) {
message = (String) req_map.get("trade_state_desc");
}
}else {
message = (String) req_map.get("err_code_des");
}
}
}
log.info("infoMsg:--- 微信支付订单查询结束");
return resp.success(return_map);
} catch (Exception e) {
log.error("erroroMsg:--- 微信支付订单查询失败" + e.getMessage() + message);
return resp.failure(e.getMessage() + message);
}
}


三、微信支付中设计的工具类
1.httpclient

package com.xunxin.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import com.alibaba.fastjson.JSON;
/**
* Copyright © 2017 noseparte(Libra) © Like the wind, like rain
* @Author Noseparte
* @Compile 2017年10月24日 -- 下午6:03:20
* @Version 1.0
* @Description   Apache HttpClient Util
*/
public class MaryunHttpUtils {

public static class UHeader{
/**
* the title of header.
*/
private String headerTitle;
private String headerValue;
public  UHeader(String headerTitle, String headerValue) {
super();
this.headerTitle = headerTitle;
this.headerValue = headerValue;
}
public String getHeaderTitle() {
return headerTitle;
}
public void setHeaderTitle(String headerKey) {
this.headerTitle = headerKey;
}
public String getHeaderValue() {
return headerValue;
}
public void setHeaderValue(String headerValue) {
this.headerValue = headerValue;
}
}
public static String getResponse(String url,HashMap args,List headerList){
String info = "";
try {
HttpClient client = new HttpClient();
//                client = setProxy(client);
//                    client.getHostConfiguration().setProxy("10.170.187.42", 3128);
client.setConnectionTimeout(60000);
client.setTimeout(60000);
GetMethod method = new GetMethod(url);
client.getParams().setContentCharset("UTF-8");
if(headerList.size()>0){
for(int i = 0;i args,List headerList){
String info = "";
try {
HttpClient client = new HttpClient();
//client = setProxy(client);
//                    client.getHostConfiguration().setProxy("10.170.187.42", 3128);
Iterator it = args.entrySet().iterator();
String sargs = "";
while(it.hasNext()){
Entry entry = (Entry)it.next();
sargs += "&"+entry.getKey().toString()+"="+entry.getValue();
}
if(!"".equals(sargs)){
sargs = "?"+sargs.substring(1, sargs.length());
}
System.out.println(url+sargs);
GetMethod method = new GetMethod(url+sargs);
//                      client.getParams().setContentCharset("UTF-8");
if(headerList!=null&&headerList.size()>0){
for(int i = 0;i map,List headerList){
String info = "";
try {
HttpClient client = new HttpClient();
//client = setProxy(client);

//                    client.getHostConfiguration().setProxy("10.170.187.42", 3128);
PostMethod method = new PostMethod(url);
client.getParams().setContentCharset("UTF-8");
if(headerList.size()>0){
for(int i = 0;i args,List headerList,String headerName){
String info = "";
try {
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);
client.getParams().setContentCharset("UTF-8");
if(headerList.size()>0){
for(int i = 0;i headerList){
String info = "";
try {
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);
client.getParams().setContentCharset("UTF-8");
if(headerList.size()>0){
for(int i = 0;i headerList,String headerName){
String info = "";
try {
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);
client.getParams().setContentCharset("UTF-8");
if(headerList.size()>0){
for(int i = 0;i= maxPerMSECSize) {
orderNumCount = 0L;
}
//组装订单号
String countStr=maxPerMSECSize +orderNumCount+"";
finOrderNum=nowLong+countStr.substring(1);
orderNumCount++;
System.out.println(finOrderNum);
// Thread.sleep(1000);
}
return finOrderNum;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


2.商户订单号工具类 OrderGeneratedUtils

import java.text.SimpleDateFormat;
import java.util.Date;
/**
*
* Copyright © 2017 noseparte(Libra) © Like the wind, like rain
* @Author Noseparte
* @Compile 2017年11月2日 -- 下午5:18:35
* @Version 1.0
* @Description   订单生成工具类
*/
public class OrderGeneratedUtils {
/**
* 锁对象,可以为任意对象
*/
private static Object lockObj = "lockerOrder";
/**
* 订单号生成计数器
*/
private static long orderNumCount = 0L;
/**
* 每毫秒生成订单号数量最大值
*/
private static int maxPerMSECSize=1000;

/**
* 生成订单编号
* @return
*/
public static synchronized String getOrderNo() {
try {
// 最终生成的订单号
String finOrderNum = "";
synchronized (lockObj) {
// 取系统当前时间作为订单号变量前半部分,精确到毫秒
long nowLong = Long.parseLong(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));
// 计数器到最大值归零,可扩展更大,目前1毫秒处理峰值1000个,1秒100万
if (orderNumCount >= maxPerMSECSize) {
orderNumCount = 0L;
}
//组装订单号
String countStr=maxPerMSECSize +orderNumCount+"";
finOrderNum=nowLong+countStr.substring(1);
orderNumCount++;
System.out.println(finOrderNum);
// Thread.sleep(1000);
}
return finOrderNum;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

 

3.微信支付随机算法生成 WXRandomNumberUtil

import java.util.Random;
/**
*
* Copyright © 2017 noseparte(Libra) © Like the wind, like rain
* @Author Noseparte
* @Compile 2017年11月6日 -- 上午11:20:41
* @Version 1.0
* @Description   微信支付生成随机数算法
*/
public class WXRandomNumberUtil {
/**
* 生成随机数算法
*
* @param length
* @return
*/
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();
}

/**
* 微信支付交易金额格式化
*
* @param amount
* @return
*/
public static int wx_format_PayAmount(String amount) {
int pay_amount = 0;
pay_amount = Integer.parseInt((amount.split(".")[0])) * 100;
return pay_amount;
}


4..微信支付工具类(官方) WXPayUtil

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import j
f346
avax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.xunxin.util.constants.WXPayConstants;
import com.xunxin.util.constants.WXPayConstants.SignType;
/**
*
* Copyright © 2017 noseparte(Libra) © Like the wind, like rain
* @Author Noseparte
* @Compile 2017年11月6日 -- 下午6:10:22
* @Version 1.0
* @Description 微信支付通用工具类
*/
public class WXPayUtil {
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map xmlToMap(String strXML) throws Exception {
try {
Map data = new HashMap();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map data, String key) throws Exception {
return generateSignedXml(data, key, SignType.MD5);
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名类型
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map data, String key, SignType signType) throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* 判断签名是否正确
*
* @param xmlStr XML格式数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
*
* @param data Map类型数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map data, String key) throws Exception {
return isSignatureValid(data, key, SignType.MD5);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名方式
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map data, String key, SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public static String generateSignature(final Map data, String key) throws Exception {
return generateSignature(data, key, SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map data, String key, SignType signType) throws Exception {
Set keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 日志
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* 获取当前时间戳,单位秒
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 获取当前时间戳,单位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
* @return
*/
public static String generateUUID() {

return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}


About Me:

Github地址:https://github.com/noseparte 
Email:  noseparte@aliyun.com 
   有java与hadoop相关的技术问题,可以发私信与我交流。
NPM地址:  https://www.npmjs.com/~noseparte
WebSite: http://www.noseparte.com/   Copyright
© 2017 noseparte
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 支付 微信 app