微信支付v3版本之app支付
来源:互联网 发布:淘宝电器店铺名字logo 编辑:程序博客网 时间:2024/05/21 09:32
首次开发微信支付,大部分代码都是参考
http://blog.csdn.net/wangqiuyun/article/details/51241064
1.公共参数配置
CommonsConstants{
public static final String APP_ID = "......";//微信开放平台审核通过的应用APPID
public static final String MCH_ID = "......";//商户号
public static final String PARTNER_KEY = ".......";//API的key
key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
public static final String CREATE_ORDER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder"; //生成支付订单的URL
public static final String QUERY_ORDER_URL="https://api.mch.weixin.qq.com/pay/orderquery"; //查询订单的URL
public static final String CLOSE_ORDER_URL="https://api.mch.weixin.qq.com/pay/closeorder"; //关闭订单的URL
public static final String NOTIFY_URL="http://........./PayController/payResult.do";//异步通知地址
public static final String BODY="云禽通商城商品";//描述
public static final String TRADE_TYPE="APP";//支付类型
}
2.Md5加密算法
import java.security.MessageDigest;
public class MD5Utils {
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" };
}
3.xml解析
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map map = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
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 = XMLUtil.getChildrenText(children);
}
map.put(k, v);
}
//关闭流
in.close();
return map;
}
/**
* 获取子结点的xml
* @param children
* @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(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
4.支付工具类
mport java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
public class WXPayUtils {
/**
* 生成count位的随机数
*
* @param count
* @return
*/
public static String getRandomString(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();
}
/**
* 定义签名,微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序
* @param characterEncoding
* @param parameters
* @return
*/
public static String createSign(String characterEncoding,SortedMap<String,String> parameters){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + CommonsConstants.PARTNER_KEY);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
String sign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
/**
* 将封装好的参数转换成Xml格式类型的字符串
* @param parameters
* @return
*/
public static String getRequestXml(SortedMap<String,String> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = 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".equalsIgnoreCase(k)){
}
else if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)) {
sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}
else {
sb.append("<"+k+">"+v+"</"+k+">");
}
}
sb.append("<"+"sign"+">"+"<![CDATA["+parameters.get("sign")+"]]></"+"sign"+">");
sb.append("</xml>");
return sb.toString();
}
/**
* 得到本地机器的IP
* @return
*/
public static String getHostIp(){
String ip = "";
try{
ip = InetAddress.getLocalHost().getHostAddress();
}catch(UnknownHostException e){
e.printStackTrace();
}
return ip;
}
/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
* @return boolean
*/
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
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(!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
//算出摘要
String mysign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
String tenpaySign = ((String)packageParams.get("sign"));
//System.out.println(tenpaySign + " " + mysign);
return tenpaySign.equals(mysign);
}
}
5.http请求工具类
这个要用到
http://archive.apache.org/dist/httpcomponents/httpclient/binary/
我用的是httpcomponents-client-4.3.1-bin版本的
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;
public class HttpUtils {
private static Logger logger = Logger.getLogger(HttpUtils.class);
public static String postHttplient(String url, String xmlInfo) {
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
HttpPost httpPost = new HttpPost(url);
HttpClientContext context = HttpClientContext.create();
StringEntity se = new StringEntity(xmlInfo, "utf-8");
se.setContentType("text/xml");
se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
"application/xml"));
se.setContentEncoding("UTF-8");
httpPost.setEntity(se);
httpPost.setConfig(RequestConfig.DEFAULT);
HttpResponse httpResponse = closeableHttpClient.execute(httpPost,
context);
HttpEntity httpEntity = httpResponse.getEntity();
// InputStream content = httpEntity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpEntity.getContent(),"utf-8"));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while ((str = reader.readLine()) != null) {
stringBuffer.append(str);
}
reader.close();
return stringBuffer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
6.统一下单接口调用
安卓前端会传(String OrderId, String totalAmount)
Map<String, Object> resultMap = new HashMap<String, Object>();
int result = 0;
float sessionmoney = Float.parseFloat(totalAmount);
String finalmoney = String.format("%.2f", sessionmoney);
finalmoney = finalmoney.replace(".", "");
int intMoney = Integer.parseInt(finalmoney);
// 封装签名参数
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", CommonsConstants.APP_ID);
packageParams.put("mch_id", CommonsConstants.MCH_ID);
packageParams.put("nonce_str", WXPayUtils.getRandomString(32));
packageParams.put("body", CommonsConstants.BODY);
packageParams.put("notify_url", CommonsConstants.NOTIFY_URL);
packageParams.put("out_trade_no", OrderId);
packageParams.put("spbill_create_ip", WXPayUtils.getHostIp());
packageParams.put("total_fee", total_fee);
packageParams.put("trade_type", CommonsConstants.TRADE_TYPE);
// 生成签名
String sign = WXPayUtils.createSign("UTF-8", packageParams);// 生成签名
packageParams.put("sign", sign);
// 生成请求的xml字符串
String requestXml = WXPayUtils.getRequestXml(packageParams);
// 发送请求返回xml字符串
String retStr = HttpUtils.postHttplient(
CommonsConstants.CREATE_ORDER_URL, requestXml);
logger.info("微信返回的xml字符串"+retStr);
Map map = XMLUtil.doXMLParse(retStr);
String return_code = (String) map.get("return_code");
String prepay_id = null;
if (return_code.contains("SUCCESS")) {
prepay_id = (String) map.get("prepay_id");// 获取到prepay_id
logger.info("微信返回的预支付Id"+prepay_id);
long currentTimeMillis = System.currentTimeMillis();//生成时间戳
long second = currentTimeMillis / 1000L;//(转换成秒)
String seconds = String.valueOf(second).substring(0, 10);//(截取前10位)
SortedMap<String, String> signParam = new TreeMap<String, String>();
signParam.put("appid", CommonsConstants.APP_ID);//app_id
signParam.put("partnerid", CommonsConstants.MCH_ID);//微信商户账号
signParam.put("prepayid", prepay_id);//预付订单id
signParam.put("package", "Sign=WXPay");//默认sign=WXPay
String returnNonce_str = WXPayUtils.getRandomString(32);
signParam.put("noncestr", returnNonce_str);//自定义不重复的长度不长于32位
signParam.put("timestamp",seconds);//北京时间时间戳
String signAgain = WXPayUtils.createSign("UTF-8", signParam);//再次生成签名
signParam.put("sign", signAgain);
返回给前端有有两种形式:
一个是封装成对象返回
对象的package属性在java里面是关键字,这里面有修改一下属性名称
PayOrderPay payOrderPay = new PayOrderPay();
payOrderPay.setAppid(CommonsConstants.APP_ID);
payOrderPay.setPrepayid(prepay_id);
payOrderPay.setPartnerid(CommonsConstants.MCH_ID);
payOrderPay.setPackageEx("Sign=WXPay");
payOrderPay.setNoncestr(returnNonce_str);
payOrderPay.setSign(signAgain);
// String timeStamp = Long.toString(new Date().getTime() / 1000);
payOrderPay.setTimestamp(seconds);
payOrderPay.setOutTradeNo(OrderId);
//拼接参数返回给移动端
// StringBuffer weiXinVo=new StringBuffer();
// weiXinVo.append("appid=").append(CommonsConstants.APP_ID).
// append("&&partnerid=").append(CommonsConstants.MCH_ID).
// append("&&prepayid=").append(prepay_id).
// append("&&package=Sign=WXPay").append("&&noncestr=").append(returnNonce_str).
// append("&×tamp=").append(seconds).
// append("&&sign=").append(signAgain).append("&&outTradeNo=").append(sb.toString());
//
// System.out.println(weiXinVo.toString());
resultMap.put("num", result);
resultMap.put("payOrderPay", payOrderPay);
}else{
String return_msg = (String) map.get("return_msg");
result = 0;
resultMap.put("num", result);
resultMap.put("reutrn_msg", return_msg);
}
7.支付回调
这个回调地址必须是外网可以直接访问的
如果你的电脑是局域网的可以做地址映射,保证微信能够直接访问到你给的URL.
我用的springmvc,直接上代码
@Controller
@RequestMapping("/PayController")
public class PayController {
private static Logger logger = Logger.getLogger(PayController.class);
@RequestMapping("/payResult")
@ResponseBody
public void payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
BufferedReader reader = null;
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();
logger.info("----接收到的数据如下:---"+xmlString);
Map<String, String> map = new HashMap<String, String>();
map = XMLUtil.doXMLParse(xmlString);
//过滤空 设置 TreeMap
SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = map.get(parameter);
String v = "";
if(null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
String key = CommonsConstants.PARTNER_KEY; // key
logger.info(packageParams);
if (WXPayUtils.isTenpaySign("UTF-8", packageParams,key)) {
String resXml = "";
if ("SUCCESS".equals((String)packageParams.get("result_code"))) {
//更新支付订单表里面的state=00 ,transaction_id,TIME_END_DTTM
|*************************我自己的业务逻辑*****************************|
String out_trade_no = (String)packageParams.get("out_trade_no");
String time_end = (String)packageParams.get("time_end");
String total_fee = (String)packageParams.get("total_fee");
String transaction_id = (String)packageParams.get("transaction_id");
payOrderService.updatePayOrderStatus(out_trade_no, total_fee, time_end, transaction_id);
logger.info("支付成功,业务参数为"+" out_trade_no = "+out_trade_no+" time_end= "+time_end+" total_fee= "+total_fee+" transaction_id= "+transaction_id);
|*************************我自己的业务逻辑*****************************|
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
}else {
//更新支付订单表里面的state=01
|*************************我自己的业务逻辑*****************************|
String out_trade_no = (String)packageParams.get("out_trade_no");
String time_end = (String)packageParams.get("time_end");
String total_fee = (String)packageParams.get("total_fee");
String transaction_id = (String)packageParams.get("transaction_id");
payOrderService.updatePayOrderStatus2(out_trade_no, total_fee, time_end, transaction_id);
logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
|*************************我自己的业务逻辑*****************************|
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} else {
logger.info("通知签名验证失败");
}
}
}
8.查询订单
// 封装签名参数
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", CommonsConstants.APP_ID);
packageParams.put("mch_id", CommonsConstants.MCH_ID);
packageParams.put("nonce_str", WXPayUtils.getRandomString(32));
packageParams.put("out_trade_no", outTradeNo);
// 生成签名
String sign = WXPayUtils.createSign("UTF-8", packageParams);
packageParams.put("sign", sign);
// 生成请求的xml字符串
String requestXml = WXPayUtils.getRequestXml(packageParams);
// 发送请求返回xml字符串
String retStr = HttpUtils.postHttplient(
CommonsConstants.QUERY_ORDER_URL, requestXml);
logger.info("微信返回的xml字符串" + retStr);
try {
Map map = XMLUtil.doXMLParse(retStr);
String return_code = (String) map.get("return_code");
if("SUCCESS".equals(return_code)){
String result_code = (String) map.get("result_code");
if("SUCCESS".equals(result_code)){
String trade_state = (String) map.get("trade_state");
if("NOTPAY".equals(trade_state)){
payOrderMapper.updatePayOrderStatus03(outTradeNo);
resultMap.put("state", "03");
}else if("REFUND".equals("trade_state")){
payOrderMapper.updatePayOrderStatus02(outTradeNo);
resultMap.put("state", "02");
}else if("CLOSED".equals("trade_state")){
payOrderMapper.updatePayOrderStatus04(outTradeNo);
resultMap.put("state", "04");
}else if("REVOKED".equals("trade_state")){
payOrderMapper.updatePayOrderStatus05(outTradeNo);
resultMap.put("state", "05");
}else if("SUCCESS".equals("trade_state")){
payOrderMapper.updatePayOrderStatus00(outTradeNo);
orderMapper.updateOrderStatus02(outTradeNo);
resultMap.put("state", "00");
}else if("USERPAYING".equals("trade_state")){
payOrderMapper.updatePayOrderStatus99(outTradeNo);
resultMap.put("state", "99");
}else if("PAYERROR".equals("trade_state")){
payOrderMapper.updatePayOrderStatus01(outTradeNo);
resultMap.put("state", "01");
}
}else{
String err_code = (String)map.get("err_code");
logger.info("微信返回err_code" + err_code);
if("ORDERNOTEXIST".equals(err_code)){
payOrderMapper.updatePayOrderStatus06(outTradeNo);
resultMap.put("state", "06");
}else{
payOrderMapper.updatePayOrderStatus07(outTradeNo);
resultMap.put("state", "07");
}
}
}else{
logger.info("通知签名验证失败");
}
- 微信支付v3版本之app支付
- V3版本微信支付
- iOS微信APP支付V3版本源码
- 微信支付 java 服务端demo (v3版本app支付 springMVC框架中)
- 微信支付 java 服务端demo (v3版本app支付 springMVC框架中)
- 微信支付 java 服务端demo (v3版本app支付 springMVC框架中)
- 微信支付 java 服务端demo (v3版本app支付 springMVC框架中)
- 微信支付 V3版本 错误总结
- 微信支付V3
- 支付--微信APP支付
- 微信支付V3版本JAVA版本js调用微信支付趟坑路线图
- 微信支付V3.3.7(jsapi支付)
- 微信支付-退款(v3版微信支付)
- 从零开始微信app支付v3-iOS版
- 微信支付 V3版
- 微信NATIVE动态支付(V3版本)
- 微信NATIVE静态支付(V3版本)
- 微信支付V3.x版本实例(上)
- git win环境配置
- 【转】TCP协议三次握手过程分析
- 我的Android成长之路(4)----利用活动管理器管理活动栈
- FM1702 13.56Mhz读卡器天线PCB设计
- (六)React Native---NavigatorIOS 组件
- 微信支付v3版本之app支付
- jquery easyui 在子tab页中打开新tab页(关于easyUI在子页面增加显示tabs的一个问题)
- 如何用 C++ 从零编写 GUI?
- POJ 2378 树形DP??
- Unity中的优化技术
- 印度无法赶超中国的原因:体制、宗教和科技,是枷锁也是动力
- MFC之屏幕录像工具
- Python菜鸟--for 循环语句
- java笔记1:数组与集合