官方统一下单参数说明
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
下载证书
1.参数配置类
package wxpay.config;
public class Constant {
/**
* 公众号AppId
*/
public static final String APP_ID = "wx2bef02******dfc";
/**
* 公众号AppSecret
*/
public static final String APP_SECRET = "b1bf********285cb55bc212c7ece97";
/**
* 微信支付商户号
*/
public static final String MCH_ID = "14******02";
/**
* 微信支付API秘钥
*/
public static final String KEY = "QBmgXTJP7QDTud**********vfYh";
/**
* 微信交易类型:公众号支付
*/
public static final String TRADE_TYPE_JSAPI = "JSAPI";
/**
* WEB
*/
public static final String WEB = "WEB";
/**
* 返回成功字符串
*/
public static final String RETURN_SUCCESS = "SUCCESS";
/**
* 支付地址(包涵回调地址)
*/
public static final String PAY_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx2bef02f0ed84edfc&redirect_uri=http%3a%2f%2fwxpay.pes-soft.com%2fwxpay%2fm%2fweChat%2funifiedOrder&response_type=code&scope=snsapi_base#wechat_redirect";
/**
* 微信统一下单url
*/
public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
* 微信申请退款url
*/
public static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
/**
* 微信支付通知url
*/
public static final String NOTIFY_URL = "http://wxpay.pes-soft.com/wxpay/";
/**
* 证书位置
*/
public static final String CERT_PATH = "H:/Ws/pes-wxpay/src/main/webapp/cert/apiclient_cert.p12";
/**
* 通过code获取授权access_token的URL
*/
public static String Authtoken_URL(String code) {
StringBuffer url = new StringBuffer();
url.append("https://api.weixin.qq.com/sns/oauth2/access_token?appid=");
url.append(Constant.APP_ID);
url.append("&secret=");
url.append(Constant.APP_SECRET);
url.append("&code=");
url.append(code);
url.append("&grant_type=authorization_code");
return url.toString();
}
}
2.用户信息实体类
package wxpay.model;
import java.io.Serializable;
public class AuthToken implements Serializable {
private static final long serialVersionUID = 8647249082921436361L;
/**
* 授权access_token
*/
private String access_token;
/**
* 有效期
*/
private String expires_in;
/**
* 刷新access_token
*/
private String refresh_token;
/**
* 用户OPENID
*/
private String openid;
/**
* 授权方式Scope
*/
private String scope;
/**
* 错误码
*/
private String errcode;
/**
* 错误消息
*/
private String errmsg;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getExpires_in() {
return expires_in;
}
public void setExpires_in(String expires_in) {
this.expires_in = expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getErrcode() {
return errcode;
}
public void setErrcode(String errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}
3.支付参数实体类
package wxpay.model;
import com.thoughtworks.xstream.annotations.XStreamAlias;
public class PaySendData {
/**
* 公众账号ID[必须]
*/
@XStreamAlias("appid")
private String appId;
/**
* 商户号[必须]
*/
@XStreamAlias("mch_id")
private String mch_id;
/**
* 设备号[WEB]
*/
@XStreamAlias("device_info")
private String device_info;
/**
* 随机字符串[必须]
*/
@XStreamAlias("nonce_str")
private String nonce_str;
/**
* 签名[必须]
*/
@XStreamAlias("sign")
private String sign;
/**
* 商品描述[必须]
*/
@XStreamAlias("body")
private String body;
/**
* 商品详情
*/
@XStreamAlias("detail")
private String detail;
/**
* 附加数据
*/
@XStreamAlias("attach")
private String attach;
/**
* 商户订单号[必须]
*/
@XStreamAlias("out_trade_no")
private String out_trade_no;
/**
* 货币类型
*/
@XStreamAlias("fee_type")
private String fee_type;
/**
* 交易金额 [必须]
*/
@XStreamAlias("total_fee")
private int total_fee;
/**
* 交易类型 [必须]
*/
@XStreamAlias("trade_type")
private String trade_type;
/**
* 通知地址 [必须]
*/
@XStreamAlias("notify_url")
private String notify_url;
/**
* 终端Ip [必须]
*/
@XStreamAlias("spbill_create_ip")
private String spbill_create_ip;
/**
* 订单生成时间yyyyMMddHHmmss
*/
@XStreamAlias("time_start")
private String time_start;
/**
* 订单失效时间yyyyMMddHHmmss 间隔>5min
*/
@XStreamAlias("time_expire")
private String time_expire;
/**
* 用户标识 tradeType=JSAPI时必须
*/
@XStreamAlias("openid")
private String openId;
/**
* 商品标记
*/
@XStreamAlias("goods_tag")
private String goods_tag;
/** 指定支付方式 */
@XStreamAlias("limit_pay")
private String limit_pay;
// ===============以下属性为申请退款参数===================
/**
* 微信订单号 [必须] 与商户退款单号二选一
*/
@XStreamAlias("transaction_id")
private String transaction_id;
/**
* 商户退款单号 [必须] 与微信单号二选一
*/
@XStreamAlias("out_refund_no")
private String out_refund_no;
/**
* 退款金额 [必须]
*/
@XStreamAlias("refund_fee")
private Integer refund_fee;
/**
* 退款货币种类
*/
@XStreamAlias("refund_fee_type")
private String refund_fee_type;
/**
* 操作员账号:默认为商户号 [必须]
*/
@XStreamAlias("op_user_id")
private String op_user_id;
public PaySendData() {
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public int getTotal_fee() {
return total_fee;
}
public void setTotal_fee(int total_fee) {
this.total_fee = total_fee;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getTime_start() {
return time_start;
}
public void setTime_start(String time_start) {
this.time_start = time_start;
}
public String getTime_expire() {
return time_expire;
}
public void setTime_expire(String time_expire) {
this.time_expire = time_expire;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getGoods_tag() {
return goods_tag;
}
public void setGoods_tag(String goods_tag) {
this.goods_tag = goods_tag;
}
public String getLimit_pay() {
return limit_pay;
}
public void setLimit_pay(String limit_pay) {
this.limit_pay = limit_pay;
}
public String getTransaction_id() {
return transaction_id;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
public String getOut_refund_no() {
return out_refund_no;
}
public void setOut_refund_no(String out_refund_no) {
this.out_refund_no = out_refund_no;
}
public Integer getRefund_fee() {
return refund_fee;
}
public void setRefund_fee(Integer refund_fee) {
this.refund_fee = refund_fee;
}
public String getRefund_fee_type() {
return refund_fee_type;
}
public void setRefund_fee_type(String refund_fee_type) {
this.refund_fee_type = refund_fee_type;
}
public String getOp_user_id() {
return op_user_id;
}
public void setOp_user_id(String op_user_id) {
this.op_user_id = op_user_id;
}
}
4.service
package wxpay.service;
import wxpay.model.AuthToken;
public interface PayService {
/**
* 统一下单接口
* @param authToken 授权token
* @param remoteAddr 请求主机ip
* @return prepayId 预支付id
*/
String unifiedOrder(AuthToken authToken, String remoteAddr);
/**
* 申请退款接口
*/
String refund();
}
serviceImpl
package wxpay.service;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import wxpay.config.Constant;
import wxpay.model.AuthToken;
import wxpay.model.PaySendData;
import wxpay.utils.HttpUtil;
import wxpay.utils.PayUtils;
@Service("payService")
public class PayServiceImpl implements PayService {
/**
* 微信支付统一下单
**/
public String unifiedOrder(AuthToken authToken, String remoteAddr) {
Map resultMap = null;
// 统一下单返回的预支付id
String prepayId = null;
PaySendData paySendData = new PaySendData();
// 构建微信支付请求参数集合
paySendData.setAppId(Constant.APP_ID);
paySendData.setMch_id(Constant.MCH_ID);
paySendData.setNotify_url(Constant.NOTIFY_URL);
paySendData.setTrade_type(Constant.TRADE_TYPE_JSAPI);
paySendData.setDevice_info(Constant.WEB);
paySendData.setBody("商品名称");
paySendData.setNonce_str(PayUtils.getRandomStr(32));
paySendData.setOut_trade_no(PayUtils.getRandomStr(8));
paySendData.setTotal_fee(1);
paySendData.setSpbill_create_ip(remoteAddr);
paySendData.setOpenId(authToken.getOpenid());
// 将参数拼成map,生产签名
paySendData.setSign(PayUtils.getSign(buildParamMap(paySendData)));
// 将请求参数对象转换成xml
String reqXml = PayUtils.sendDataToXml(paySendData);
try {
// 发送请求
CloseableHttpResponse response = HttpUtil.Post(Constant.UNIFIED_ORDER_URL, reqXml, false);
try {
resultMap = PayUtils.parseXml(response.getEntity().getContent());
// 关闭流
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} catch (Exception e) {
System.out.println("微信支付统一下单异常");
}
String return_code = resultMap.get("return_code");
String result_code = resultMap.get("result_code");
if (Constant.RETURN_SUCCESS.equals(return_code) && Constant.RETURN_SUCCESS.equals(result_code)) {
// return_code=通信标识
// result_code=交易标识
// 只有当returnCode与resultCode均返回“success”,才代表微信支付统一下单成功
prepayId = "prepay_id=" + resultMap.get("prepay_id");
}
return prepayId;
}
public String refund() {
PaySendData paySendData = new PaySendData();
// 构建微信支付请求参数集合
paySendData.setAppId(Constant.APP_ID);
paySendData.setMch_id(Constant.MCH_ID);
paySendData.setNonce_str(PayUtils.getRandomStr(32));
// paySendData.setTransaction_id(WeChatUtils.getRandomStr(32));
// Out_trade_no与Transaction_id二选一
paySendData.setOut_trade_no("jtVFcgbM");
paySendData.setOut_refund_no("jtVFcgbM");
paySendData.setTotal_fee(1);
paySendData.setRefund_fee(1);
paySendData.setOp_user_id(Constant.MCH_ID);
// 将参数拼成map,生产签名
paySendData.setSign(PayUtils.getSign(buildParamMap(paySendData)));
// 将请求参数对象转换成xml
String reqXml = PayUtils.sendDataToXml(paySendData);
try {
// 发送请求
CloseableHttpResponse response = HttpUtil.Post(Constant.UNIFIED_ORDER_URL, reqXml, true);
try {
// 关闭流
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} catch (Exception e) {
System.out.println("微信退款异常");
}
return null;
}
/**
* 构建统一下单参数map 用于生成签名
*
* @param data
* @return SortedMap
*/
private SortedMap buildParamMap(PaySendData data) {
SortedMap paramters = new TreeMap();
Field[] fields = data.getClass().getDeclaredFields();
try {
for (Field field : fields) {
field.setAccessible(true);
if (null != field.get(data)) {
paramters.put(field.getName().toLowerCase(), field.get(data).toString());
}
}
} catch (Exception e) {
System.out.print("构建签名map错误: ");
e.printStackTrace();
}
return paramters;
}
}
5.主页controller
package wxpay.ctrl;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import wxpay.config.Constant;
@Controller
@RequestMapping(value = "/")
public class IndexController {
@RequestMapping(value = "/")
public String index(Model model) {
model.addAttribute("payURL", Constant.PAY_URL);
return "index";
}
}
支付controller
package wxpay.ctrl;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import wxpay.config.Constant;
import wxpay.model.AuthToken;
import wxpay.service.PayService;
import wxpay.utils.PayUtils;
@Controller
@RequestMapping(value = "/m/weChat")
public class WeChatOrderController {
@Autowired
private PayService payService;
/**
* 统一下单
*/
@RequestMapping(value = "/unifiedOrder")
public String unifiedOrder(HttpServletRequest request, Model model) {
// 用户同意授权,获得的code
String code = request.getParameter("code");
// 通过code获取网页授权access_token
AuthToken authToken = PayUtils.getTokenByAuthCode(code);
// 调用统一下单service
String prepayId = payService.unifiedOrder(authToken, request.getRemoteAddr());
if (!PayUtils.isEmpty(prepayId)) {
String timeStamp = PayUtils.getTimeStamp();// 当前时间戳
String nonceStr = PayUtils.getRandomStr(20);// 不长于32位的随机字符串
SortedMap signMap = new TreeMap();// 自然升序map
signMap.put("appId", Constant.APP_ID);
signMap.put("package", prepayId);
signMap.put("timeStamp", timeStamp);
signMap.put("nonceStr", nonceStr);
signMap.put("signType", "MD5");
model.addAttribute("appId", Constant.APP_ID);
model.addAttribute("timeStamp", timeStamp);
model.addAttribute("nonceStr", nonceStr);
model.addAttribute("prepayId", prepayId);
model.addAttribute("paySign", PayUtils.getSign(signMap));// 获取签名
} else {
System.out.println("微信统一下单失败,订单编号:失败原因");
return "redirect:/";// 支付下单失败,重定向至订单列表
}
// 将支付需要参数返回至页面,采用h5方式调用支付接口
return "h5Pay";
}
/**
* 申请退款
*/
@RequestMapping(value = "/refund")
public String refund(HttpServletRequest request, Model model) {
// 调用统一下单service
payService.refund();
return "h5Pay";
}
}
6.工具类
https://pan.baidu.com/s/1qWunw5piOxCeZynHIGMQWg 密码:wy31
7.页面
https://pan.baidu.com/s/15v0cWoXmjJJLF9L_4h17Ng 密码:1p6g