应用场景
随着互联网发展,众多网站都链接了微信、支付宝等支付方式,今天讲的是本人在做微信支付扫码支付时所遇到的坑,自我感觉比其他一些文档讲解的细致多,有用就拿去,没用也别喷,谢谢、
下单支付
可以先看一下文档,有个基本认知:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1
1、必备条件
首先你要要申请一个公共账号,最好申请的时候就是服务号,因为微信支付的前提必须是服务号
如果是公众号需要先申请验证才能升级为服务号。
1.1、微信服务号申请地址:
http://www.weixinvip888.com/index.php?s=/index/wx.html
1.2、扫码支付流程
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1
2、引入相关的jar包(此章讲解的扫码支付模式二)
<!-- 生成二维码所需的jar包 开始 -->
<dependency>
<groupId>com.uqihong</groupId>
<artifactId>qdcode</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.uqihong</groupId>
<artifactId>qdcodeSwetake</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 生成二维码所需的jar包 结束 -->
<!-- 微信支付需要的jar包 -->
<dependency>
<groupId>xmlpull</groupId>
<artifactId>xmlpull</artifactId>
<version>1.1.3.1</version>
</dependency>
<dependency>
<groupId>xpp3</groupId>
<artifactId>xpp3</artifactId>
<version>1.1.4c</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>4.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-cache</artifactId>
<version>4.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.5</version>
</dependency>
3、微信扫码支付所需参数
public class WeixinPayConfigure {
/**
* 域名 项目域名
*/
public static final String ROOTURL = "http://www.uxuexi.com";
/**
* 域名 请求微信时的路径
*/
public static final String ORDER_ROOTURL = "http://order.uxuexi.com";
/**
* 商户id (商户号)
*/
public static final String MCH_ID = "1111111111";
/**
* 公共账号id (18位数↓)
*/
public static final String APPID = "wx111111111111111c";
/**
* 应用秘钥(AppSecret) 在公众号平台上找↓
*/
public static final String APP_SECRET = "f4e04138de2b34b75a645c13a654f815";
/**API秘钥*/
public static final String API_KEY = "shemadongxi1243432sdf3213";
/**
* 统一下单URL
*/
public static final String PAY_UNIFIED_ORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
* 微信公众号交易类型 (扫码支付类型:NATIVE,公众号支付类型:JSAPI)
*/
public static final String TRADE_TYPE = "NATIVE";
/**
* 微信支付成功之后的回调 (微信支付成功后调用)
*/
public static final String NOTIFY_ACTIVITY_URL = ORDER_ROOTURL + "/pay/wx/wxnotify.json";
}
4、微信支付操作(这步操作需商品订单创建成功后执行)
①、
订单创建成功去支付(PC端)
@At
public Object gowxPay(@Param("orderId") long orderId, @Param("payPrice") double payPrice) {
//此处返回的是二维码
return weChatPayViewService.getOrCode(orderId);
}
②、获取二维码所需的预支付订单路径
public Object getOrCode(long orderId) {
ExceptionUtil.checkId(orderId, "订单id不能为空");
//获取二维码
return getWxPayCodeUrl(orderId);
}
③、拼接预支付订单路径
private String getWxPayCodeUrl(long orderId) {
OrderEntity order = orderBusinessService.getWithEx(orderId);
double paymentPrice = order.getPayPrice();
PayEntity payEntity = dbDao.fetch(PayEntity.class, PAY_SWITCH_KEY);
if (payEntity.isPay()) {
paymentPrice = payEntity.getMoney();
}
//拼接预支付路径(二维码所需路径)dowithWxReturn():处理微信返回,sendReqGetPreOrder():拼接
return dowithWxReturn(sendReqGetPreOrder(ConvertUtil.obj2long(orderId), paymentPrice));
}
④、处理微信返回
private String dowithWxReturn(String result) {
String codeUrl = "";
Map<String, Object> weixinPrepayInfo = MapUtil.map();
try {
//解析XML(工具类1)
weixinPrepayInfo = XMLParser.getMapFromXML(result);
String return_code = (String) weixinPrepayInfo.get("return_code");
if ("SUCCESS".equals(return_code)) {
codeUrl = (String) weixinPrepayInfo.get("code_url");
//非空判断↓
if (Util.isEmpty(codeUrl)) {
//-----------------------------------对象转换成字符串,(工具类2)↓
throw ExceptionUtil.bEx(ConvertUtil.obj2str(weixinPrepayInfo.get("err_code_des")));
}
return codeUrl;
} else {
throw ExceptionUtil.bEx("预支付失败");
}
} catch (Exception e) {
ExceptionUtil.bEx("调用微信预支付接口出错");
}
return codeUrl;
}
⑤、拼接微信预支付路径所需参数
public String sendReqGetPreOrder(long orderId, double payPrice) {
ExceptionUtil.checkEmpty(orderId, "订单id不能为空");
int randomNumLength = 32;
Map<String, Object> params = MapUtil.map();
//公共账号id
params.put("appid", WeixinPayConfigure.APPID);
//商户id
params.put("mch_id", WeixinPayConfigure.MCH_ID);
//商品描述
params.put("body", "优学习课程");
//设备号
//ctrl+alt+T
params.put("device_info", "WEB");
//随机字符串 产生随机字符串 字符串由26个字母以及数字构成(该方法见工具类↓)
params.put("nonce_str", RandomUtil.randomString(randomNumLength));
//订单编号
params.put("out_trade_no", orderId);
//金额
double paymentPrice = payPrice;
//-----------------------(工具类3)将元为单位的转换为分 (乘100)↓
params.put("total_fee", AmountUtils.changeY2F(paymentPrice));
//回调地址
params.put("notify_url", WeixinPayConfigure.NOTIFY_ACTIVITY_URL);
//公众号交易类型
params.put("trade_type", WeixinPayConfigure.TRADE_TYPE);
//签名 (工具类4)
String sign = Signature.getSign(params, WeixinPayConfigure.API_KEY);
params.put("sign", sign);
//---------------(工具类5)
return HttpRequest.sendPost(WeixinPayConfigure.PAY_UNIFIED_ORDER_API, params);
}
5、生成二维码
①、二维码有两种生成方式,
二、把微信返回的路径(URL)打回前端,前端生成二维码插件QRCode.js(
资料:http://code.ciaoca.com/javascript/qrcode/)
②、此章生成二维码则是运用的前端js生成
注意:记得引入插件
<script src="qrcode.js">
</script>
$ajax.weAjax({
url:we.vr.order+'/pay/wx/gowxPay.html?orderId='+data,
data:{},
success:function(data){
$('#qrCode').empty();
var qrcode = new QRCode(document.getElementById("qrCode"), data)
$wedialog.dialog({
title:'微信支付',
content:$('.qrCode')
});
}
})
支付成功
1、当用户扫码支付成功后,微信会调取我们配置的成功后的回调函数
①、调用成功后的回调函数
@At
@Filters
public void wxnotify() throws Exception {
weChatPayViewService.weChatPayViewService();
}
②、微信提醒,签名校验校验
public void weChatPayViewService() throws Exception {
HttpServletRequest request = Mvcs.getReq();
HttpServletResponse response = Mvcs.getResp();
//获取微信响应的内容
String responseString = getWeiXinResponseContent(request);
PrintWriter out = response.getWriter();
String resp = "";
String signKey = WeixinPayConfigure.API_KEY;
//---------------------签名算法,见工具类4
boolean verify = Signature.checkIsSignValidFromResponseString(responseString, signKey);
if (!verify) {
logger.error("签名验证失败");
resp = "签名验证失败";
out.write(resp);
out.close();
return;
}
//解析微信返回的XML文件↓
Map<String, Object> map = XMLParser.getMapFromXML(responseString);
String result_code = ConvertUtil.obj2str(map.get("result_code"));
if (!"SUCCESS".equalsIgnoreCase(result_code)) {
//--------------获取响应给微信的xml数据↓(工具类7)
resp = PayCommonUtil.getResponseXML("ERROR", "ERROR");
out.write(resp);
out.close();
return;
}
//签名校验成功,处理订单
resp = handleOrder(map);
out.write(resp);
out.close();
}
③、处理订单
@Aop("txDb")
private String handleOrder(Map<String, Object> map) throws Exception {
//获取响应给微信的xml数据↓
String resp = PayCommonUtil.getResponseXML("SUCCESS", "OK");
//微信支付订单↓
String transaction_id = ConvertUtil.obj2str(map.get("transaction_id"));
//支付完成时间↓
String time_end = ConvertUtil.obj2str(map.get("time_end"));
//商户订单号↓
String out_trade_no = (String) map.get("out_trade_no");
//if逻辑,如果上述三个参数为空说明此订单支付失败,直接return
if (Util.isEmpty(transaction_id) || Util.isEmpty(time_end) || Util.isEmpty(out_trade_no)) {
resp = PayCommonUtil.getResponseXML("ERROR", "参数错误,微信支付订单号、支付完成时间、订单号均不能为空");
return resp;
}
//根据订单号获取订单
OrderEntity order = orderBusinessService.get(ConvertUtil.obj2long(out_trade_no));
//判断该订单是否存在
if (Util.isEmpty(order)) {
resp = PayCommonUtil.getResponseXML("ERROR", "订单不存在");
return resp;
}
//获取订单状态
int orderStatus = order.getStatus();
//如果订单状态是“已完成”,说明此订单已被处理过则无需再处理,直接return
if (WxOrderStatusEnum.FINISHED.intKey() == orderStatus) {
return resp;
}
//如果此订单是为付款,则说明此订单没有被处理过,if里面可以写自己所需要的业务
if (WxOrderStatusEnum.WAITING_PAY.intKey() == orderStatus) {
//此处写你所需的业务即可,比如:更改订单状态、添加购买记录、添加支付记录等
}
return resp;
}
④、当支付成功,订单完成之后,则需要关闭二维码弹框,当然,微信本身是不可能来实现这一步的,所以还是需要我们自己来处理,
这里暂时写了个定时器,根据查询订单状态来确认订单是否完成,完成、则关闭二维码
window.setInterval(function () {
$.get(we.vr.order+'/pay/wx/queryorder.html?orderId='+data,
{"timed": new Date().getTime()},
function (data) {
if(data.isPay == true){
$('.ui-dialog-close').click();
we.utils.gotoUrl(we.vr.kecheng+'/student/mycourse.html');
}
});
}, 3000);
②.①、获取微信响应的内容
private String getWeiXinResponseContent(HttpServletRequest request) throws IOException,
UnsupportedEncodingException {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
//获取微信调用我们notify_url的返回信息
String responseString = new String(outStream.toByteArray(), "utf-8");
outStream.close();
inStream.close();
return responseString;
}
相关工具类
1、解析XML工具类
public class XMLParser {
public static Map<String, Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException,
SAXException {
//这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = Utils.getStringStream(xmlString);
Document document = builder.parse(is);
//获取到document里面的全部结点
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
Map<String, Object> map = new HashMap<String, Object>();
int i = 0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if (node instanceof Element) {
map.put(node.getNodeName(), node.getTextContent());
}
i++;
}
return map;
}
}
2、生成随机字符串
//生成随机字符串,字符串由26个字母以及数字构成
public static String randomString(final int lengths) {
return random(lengths, STRING_CODE);
}
3、金额工具类
public class AmountUtils {
/**金额为分的格式 */
public static final String CURRENCY_FEN_REGEX = "\\-?[0-9]+";
/**
* 将分为单位的转换为元并返回金额格式的字符串 (除100)
*
* @param amount
* @return
* @throws Exception
*/
public static String changeF2Y(Long amount) throws Exception {
if (!amount.toString().matches(CURRENCY_FEN_REGEX)) {
throw new Exception("金额格式有误");
}
int flag = 0;
String amString = amount.toString();
if (amString.charAt(0) == '-') {
flag = 1;
amString = amString.substring(1);
}
StringBuffer result = new StringBuffer();
if (amString.length() == 1) {
result.append("0.0").append(amString);
} else if (amString.length() == 2) {
result.append("0.").append(amString);
} else {
String intString = amString.substring(0, amString.length() - 2);
for (int i = 1; i <= intString.length(); i++) {
if ((i - 1) % 3 == 0 && i != 1) {
result.append(",");
}
result.append(intString.substring(intString.length() - i, intString.length() - i + 1));
}
result.reverse().append(".").append(amString.substring(amString.length() - 2));
}
if (flag == 1) {
return "-" + result.toString();
} else {
return result.toString();
}
}
/**
* 将分为单位的转换为元 (除100)
*
* @param amount
* @return
* @throws Exception
*/
public static String changeF2Y(String amount) throws Exception {
if (!amount.matches(CURRENCY_FEN_REGEX)) {
throw new Exception("金额格式有误");
}
return BigDecimal.valueOf(Long.valueOf(amount)).divide(new BigDecimal(100)).toString();
}
/**
* 将元为单位的转换为分 (乘100)
*
* @param amount
* @return
*/
public static String changeY2F(Long amount) {
return BigDecimal.valueOf(amount).multiply(new BigDecimal(100)).toString();
}
/**
* 将元为单位的转换为分 (乘100)
*
* @param amount
* @return
*/
public static long changeY2F(Double amount) {
return Math.round(amount * 100);
}
/**
* 将元为单位的转换为分 替换小数点,支持以逗号区分的金额
*
* @param amount
* @return
*/
public static String changeY2F(String amount) {
String currency = amount.replaceAll("\\$|\\¥|\\,", ""); //处理包含, ¥ 或者$的金额
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if (index == -1) {
amLong = Long.valueOf(currency + "00");
} else if (length - index >= 3) {
amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
} else if (length - index == 2) {
amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
} else {
amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
}
return amLong.toString();
}
}
4、
微信支付签名算法工具类
5、
HttpRequest工具类
public class Signature {
/**
* 签名算法
* @param o 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class<?> cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != "") {
list.add(f.getName() + "=" + f.get(o) + "&");
}
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();
Utils.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
Utils.log("Sign Result:" + result);
return result;
}
public static String getSign(Map<String, Object> map, String key) {
ArrayList<String> list = new ArrayList<String>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() != "") {
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + key;
//Util.log("Sign Before MD5:" + result);
System.out.println("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
System.out.println("Sign Result:" + result);
//Util.log("Sign Result:" + result);
return result;
}
/**
* 从API返回的XML数据里面重新计算一次签名
* @param responseString API返回的XML数据
* @return 新鲜出炉的签名
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static String getSignFromResponseString(String responseString, String key) throws IOException, SAXException,
ParserConfigurationException {
Map<String, Object> map = XMLParser.getMapFromXML(responseString);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign", "");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
return Signature.getSign(map, key);
}
/**
* 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
* @param responseString API返回的XML数据字符串
* @return API签名是否合法
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static boolean checkIsSignValidFromResponseString(String responseString, String key)
throws ParserConfigurationException, IOException, SAXException {
Map<String, Object> map = XMLParser.getMapFromXML(responseString);
Utils.log(map.toString());
String signFromAPIResponse = map.get("sign").toString();
if (signFromAPIResponse == "" || signFromAPIResponse == null) {
Utils.log("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
Utils.log("服务器回包里面的签名是:" + signFromAPIResponse);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign", "");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
String signForAPIResponse = Signature.getSign(map, key);
if (!signForAPIResponse.equals(signFromAPIResponse)) {
//签名验不过,表示这个API返回的数据有可能已经被篡改了
Utils.log("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
return false;
}
Utils.log("恭喜,API返回的数据签名验证通过!!!");
return true;
}
}
public class HttpRequest {
private static Log log = new Log(LoggerFactory.getLogger(HttpRequest.class));
public static String sendPost(String url, Map<String, Object> parameters) {
String result = null;
HttpPost httpPost = new HttpPost(url);
//将要提交给API的数据对象转换成XML格式数据Post给API
String postDataXML = PayCommonUtil.getRequestXml(parameters);
Utils.log("API,POST过去的数据是:");
Utils.log(postDataXML);
//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
Utils.log("executing request" + httpPost.getRequestLine());
try {
//非ssl连接
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
} catch (ConnectionPoolTimeoutException e) {
log.e("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
log.e("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
log.e("http get throw SocketTimeoutException");
} catch (Exception e) {
log.e("http get throw Exception");
} finally {
httpPost.abort();
}
return result;
}
}
6、二维码工具类
public class QrcodeUtil {
public static boolean encoderQRCode(String content, String imgPath) {
ExceptionUtil.checkEmpty(content, "链接不能为空");
boolean isSuccess = true;
try {
Qrcode qrcodeHandler = new Qrcode();
// 设置二维码排错率,可选L(7%)、M(15%)、Q(25%)、H(30%),排错率越高可存储的信息越少,但对二维码清晰度的要求越小
qrcodeHandler.setQrcodeErrorCorrect('L');
qrcodeHandler.setQrcodeEncodeMode('B');
qrcodeHandler.setQrcodeVersion(7);
// int imgSize = 67 + 12 * (size - 1);
byte[] contentBytes = content.getBytes("gb2312");
BufferedImage bufImg = new BufferedImage(215, 215, BufferedImage.TYPE_INT_RGB);
Graphics2D gs = bufImg.createGraphics();
gs.setBackground(Color.WHITE);
gs.clearRect(0, 0, 215, 215);
// 设定图像颜色> BLACK
gs.setColor(Color.BLACK);
// 设置偏移量 不设置可能导致解析出错
int pixoff = 2;
// 输出内容> 二维码
if (contentBytes.length > 0 && contentBytes.length < 800) {
boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);
for (int i = 0; i < codeOut.length; i++) {
for (int j = 0; j < codeOut.length; j++) {
if (codeOut[j][i]) {
gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);
}
}
}
} else {
System.err.println("QRCode content bytes length = " + contentBytes.length + " not in [ 0,120 ]. ");
}
gs.dispose();
bufImg.flush();
// 生成二维码QRCode图片
ImageIO.write(bufImg, "png", new File(imgPath));
return isSuccess;
} catch (Exception e) {
isSuccess = false;
e.printStackTrace();
return isSuccess;
}
}
/**
* 获取二维码存放的位置
*
* @param kvConfig
* @param file
* @param upload
* @param foldPath
* @param fileName 文件名(有文件可以不传这个名称)
* @return 路径
*/
public static String getQrCodePath(KvConfig kvConfig, IUpload upload) {
DateTime dt = DateTimeUtil.now();
final String fileServer = kvConfig.getValue("file_server");
StringBuilder sb = new StringBuilder(fileServer).append(File.separator);
if (!Util.isEmpty(upload)) {
sb.append(upload.foldName()).append(File.separator);
}
String realFoldPath = dt.getYear() + File.separator + dt.getMonthOfYear() + File.separator + dt.getDayOfMonth()
+ File.separator;
sb.append(realFoldPath);
sb.append(RandomUtil.uu16() + ".png");
FileUtil.createNewFile(new File(sb.toString()));
return StringUtil.path2Web(sb.toString());
}
/**
*
* 得到路径
*
* @param path
* @param kvConfig
*/
public static String getRelationPath(String path, KvConfig kvConfig) {
if (Util.isEmpty(path)) {
return "";
}
File f = new File(kvConfig.getValue("file_server"));
return StringUtil.removeLeft(StringUtil.path2Web(path), StringUtil.path2Web(f.getPath()));
}
}
7、支付工具类
public class PayCommonUtil {
/**
* 获取请求微信接口时的xml数据
*/
public static String getRequestXml(Map<String, Object> params) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = params.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = entry.getValue().toString();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 获取响应给微信的xml数据
*/
public static String getResponseXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg
+ "]]></return_msg></xml>";
}
}
下面链接是微信公众号支付