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

微信公众号支付开发-JAVA版DEMO

2017-10-09 16:55 381 查看

1.准备工作

首先登录微信公众平台,获取并配置以下微信开发配置:

开发者ID【AppID和AppSecret】
服务器配置

1.url服务器地址设置

2.Token【自己设置,必须英文或数字】

3.EncodingAESKey[自己随机生成,用于消息加解密]
然后登录微信商户平台,获取并配置以下微信支付配置:

商户号(mchId)
API秘钥(key)
API证书(java版主要使用:apiclient_cert.p12)
 PS:需要微信公众号支付银行对私接口,请联系电话/微信17605918869

2.代码展示

提醒:此处粘贴出的代码为方便初学者比较直观的了解、学习微信公众号支付,部分代码并未按照编码规范封装成方法、工具类

将微信支付所有参数定义为 WeChatConfig.java

public class WeChatConfig {
/**公众号AppId*/
public static final APP_ID = "";

/**公众号AppSecret*/
public static final APP_SECRET = "";

/**微信支付商户号*/
public static final String MCH_ID = "";

/**微信支付API秘钥*/
public static final String KEY = "";

/**微信支付api证书路径*/
public static final String CERT_PATH = "***/apiclient_cert.p12";

/**微信统一下单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 = "此处url用于接收微信服务器发送的支付通知,并处理商家的业务";

/**微信交易类型:公众号支付*/
public static final String TRADE_TYPE_JSAPI = "JSAPI";

/**微信交易类型:原生扫码支付*/
public static final String TRADE_TYPE_NATIVE = "NATIVE";

/**微信甲乙类型:APP支付*/
public static final String TRADE_TYPE_APP = "APP";
}

处理微信公众号支付请求的Controller:WeChatOrderController.java

@RequestMapping(value="/m/weChat/")
@Controller("weChatOrderController")
public class WeChatOrderController{

@Autowired
private OrderService orderService;
@Autowired
private WechatPayService wechatPayService;
@Autowired
private NotifyReturnService notifyReturnService;

@RequestMapping(value = "unifiedOrder")
public String unifiedOrder(HttpServletRequest request,Model model){
//用户同意授权,获得的code
String code = request.getParameter("code");
//请求授权携带的参数【根据自己需要设定值,此处我传的是订单id】
String state = request.getParameter("state");
Order order = orderService.get(state);//订单信息
//通过code获取网页授权access_token
AuthToken authToken = WeChatUtils.getTokenByAuthCode(code);
//构建微信统一下单需要的参数
Map<String,Object> map = Maps.newHashMap();
map.put("openId",authToken.getOpenid());//用户标识openId
map.put("remoteIp",request.getRemoteAddr());//请求Ip地址
//调用统一下单service
Map<String,Object> resultMap = WeChatPayService.unifiedOrder(order,map);
String returnCode = (String) resultMap.get("return_code");//通信标识
String resultCode = (String) resultMap.get("result_code");//交易标识
//只有当returnCode与resultCode均返回“success”,才代表微信支付统一下单成功
if (WeChatConstant.RETURN_SUCCESS.equals(resultCode)&&WeChatConstant.RETURN_SUCCESS.equa
4000
ls(returnCode)){
String appId = (String) resultMap.get("appid");//微信公众号AppId
String timeStamp = WeChatUtils.getTimeStamp();//当前时间戳
String prepayId = "prepay_id="+resultMap.get("prepay_id");//统一下单返回的预支付id
String nonceStr = WeChatUtils.getRandomStr(20);//不长于32位的随机字符串
SortedMap<String,Object> signMap = Maps.newTreeMap();//自然升序map
signMap.put("appId",appId);
signMap.put("package",prepayId);
signMap.put("timeStamp",timeStamp);
signMap.put("nonceStr",nonceStr);
signMap.put("signType","MD5");
model.addAttribute("appId",appId);
model.addAttribute("timeStamp",timeStamp);
model.addAttribute("nonceStr",nonceStr);
model.addAttribute("prepayId",prepayId);
model.addAttribute("paySign",WeChatUtils.getSign(signMap));//获取签名
}else {
logger.error("微信统一下单失败,订单编号:"+order.getOrderNumber()+",失败原因:"+resultMap.get("err_code_des"));
return "redirect:/m/orderList";//支付下单失败,重定向至订单列表
}
//将支付需要参数返回至页面,采用h5方式调用支付接口
return "/mobile/order/h5Pay";
}
}

微信支付前端发起页面: weChatPayTest.jsp

支付按钮href中的redirect_uri= http://自己服务的ip或者域名/m/weChat/unifiedOrder 强调部分需要进行uriEncode
此处代码为在微信公众号内网页调用,故使用的是微信网页授权方式,将订单id通过支付接口中state参数进行传递
微信网页授权说明
<!DOCTYPE HTML>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
<meta name="screen-orientation" content="portrait">
<meta name="x5-orientation" content="portrait">
<link rel="stylesheet" href="/static/weui/dist/style/weui.min.css">
<title>微信公众号支付测试</title>
</head>
<body>
<div class="container" id="container">
<a href="https://open.weixin.qq.com/connect/oauth2/authorizeappid=wx67e9c91f0bac335d&redirect_uri=http%3a%2f%2f***%2fm%2fweChat%2funifiedOrder&response_type=code&scope=snsapi_base&state=${order.id}#wechat_redirect" class="weui_btn weui_btn_primary">立即支付</a>
</div>
</body>
</html>

h5方式调用微信支付接口:h5Pay.jsp

WeixinJSBridge为微信公众号内置对象,所以必须在公众号内部网页使用
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>

1cf49
<title>确认支付</title>
<script type="text/javascript" src="/static/jquery/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="/static/jquery-plugin/jquery.form.js"></script>
</head>
<body>
<input type="hidden" name="appId" value="${appId}">
<input type="hidden" name="nonceStr" value="${nonceStr}">
<input type="hidden" name="prepayId" value="${prepayId}">
<input type="hidden" name="paySign" value="${paySign}">
<input type="hidden" name="timeStamp" value="${timeStamp}">
</body>
<script>

function onBridgeReady(){
var appId = $("input[name='appId']").val();
var nonceStr = $("input[name='nonceStr']").val();
var prepayId = $("input[name='prepayId']").val();
var paySign = $("input[name='paySign']").val();
var timeStamp = $("input[name='timeStamp']").val();
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":appId,
"timeStamp":timeStamp,
"nonceStr":nonceStr,
"package":prepayId,
"signType":"MD5",
"paySign":paySign
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
location.href="支付成功返回商家自定义页面";
}else {//这里支付失败和支付取消统一处理
alert("支付取消");
location.href="支付失败返回商家自定义页面";
}
}
);
}

$(document).ready(function () {
if (typeof WeixinJSBridge == "undefined"){
if (document.addEventListener){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else {
onBridgeReady();
}
});
</script>
</html>

微信支付订单Service:WeChatPayService.java

微信支付API–统一下单 使用说明
/**
*微信支付统一下单
**/
public Map<String,Object> unifiedOrder(Order order, Map<String,Object> map){
Map<String,Object> resultMap;
try {
WxPaySendData paySendData = new WxPaySendData();
//构建微信支付请求参数集合
paySendData.setAppId(WeChatConstant.APP_ID);
paySendData.setAttach("微信订单支付:"+order.getOrderNumber());
paySendData.setBody("商品描述");
paySendData.setMchId(WeChatConfig.MCH_ID);
paySendData.setNonceStr(WeChatUtils.getRandomStr(32));
paySendData.setNotifyUrl(WeChatConfig.NOTIFY_URL);
paySendData.setDeviceInfo("WEB");
paySendData.setOutTradeNo(order.getOrderNumber());
paySendData.setTotalFee(order.getSumFee());
paySendData.setTradeType(WeChatConfig.TRADE_TYPE_JSAPI);
paySendData.setSpBillCreateIp((String) map.get("remoteIp"));
paySendData.setOpenId((String) map.get("openId"));
//将参数拼成map,生产签名
SortedMap<String,Object> params = buildParamMap(paySendData);
paySendData.setSign(WeChatUtils.getSign(params));
//将请求参数对象转换成xml
String reqXml = WeChatUtils.sendDataToXml(paySendData);
//发送请求
byte[] xmlData = reqXml.getBytes();
URL url = new URL(WeChatConfig.UNIFIED_ORDER_URL);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content_Type","text/xml");
urlConnection.setRequestProperty("Content-length",String.valueOf(xmlData.length));
DataOutputStream outputStream = new DataOutputStream(urlConnection.getOutputStream());
outputStream.write(xmlData);
outputStream.flush();
outputStream.close();
resultMap = WeChatUtils.parseXml(urlConnection.getInputStream());
} catch (Exception e) {
throw new ServiceException("微信支付统一下单异常",e);
}
return resultMap;

/**
* 构建统一下单参数map 用于生成签名
* @param data
* @return SortedMap<String,Object>
*/
private SortedMap<String,Object> buildParamMap(WxPaySendData data) {
SortedMap<String,Object> paramters = new TreeMap<String, Object>();
if (null != data){
if (StringUtils.isNotEmpty(data.getAppId())){
paramters.put("appid",data.getAppId());
}
if (StringUtils.isNotEmpty(data.getAttach())){
paramters.put("attach",data.getAttach());
}
if (StringUtils.isNotEmpty(data.getBody())){
paramters.put("body",data.getBody());
}
if (StringUtils.isNotEmpty(data.getDetail())){
paramters.put("detail",data.getDetail());
}
if (StringUtils.isNotEmpty(data.getDeviceInfo())){
paramters.put("device_info",data.getDeviceInfo());
}
if (StringUtils.isNotEmpty(data.getFeeType())){
paramters.put("fee_type",data.getFeeType());
}
if (StringUtils.isNotEmpty(data.getGoodsTag())){
paramters.put("goods_tag",data.getGoodsTag());
}
if (StringUtils.isNotEmpty(data.getLimitPay())){
paramters.put("limit_pay",data.getLimitPay());
}
if (StringUtils.isNotEmpty(data.getMchId())){
paramters.put("mch_id",data.getMchId());
}
if (StringUtils.isNotEmpty(data.getNonceStr())){
paramters.put("nonce_str",data.getNonceStr());
}
if (StringUtils.isNotEmpty(data.getNotifyUrl())){
paramters.put("notify_url",data.getNotifyUrl());
}
if (StringUtils.isNotEmpty(data.getOpenId())){
paramters.put("openid",data.getOpenId());
}
if (StringUtils.isNotEmpty(data.getOutTradeNo())){
paramters.put("out_trade_no",data.getOutTradeNo());
}
if (StringUtils.isNotEmpty(data.getSign())){
paramters.put("sign",data.getSign());
}
if (StringUtils.isNotEmpty(data.getSpBillCreateIp())){
paramters.put("spbill_create_ip",data.getSpBillCreateIp());
}
if (StringUtils.isNotEmpty(data.getTimeStart())){
paramters.put("time_start",data.getTimeStart());
}
if (StringUtils.isNotEmpty(data.getTimeExpire())){
paramters.put("time_expire",data.getTimeExpire());
}
if (StringUtils.isNotEmpty(data.getProductId())){
paramters.put("product_id",data.getProductId());
}
if (data.getTotalFee()>0){
paramters.put("total_fee",data.getTotalFee());
}
if (StringUtils.isNotEmpty(data.getTradeType())){
paramters.put("trade_type",data.getTradeType());
}
//申请退款参数
if (StringUtils.isNotEmpty(data.getTransactionId())){
paramters.put("transaction_id",data.getTransactionId());
}
if (StringUtils.isNotEmpty(data.getOutRefundNo())){
paramters.put("out_refund_no",data.getOutRefundNo());
}
if (StringUtils.isNotEmpty(data.getOpUserId())){
paramters.put("op_user_id",data.getOpUserId());
}
if (StringUtils.isNotEmpty(data.getRefundFeeType())){
paramters.put("refund_fee_type",data.getRefundFeeType());
}
if (null != data.getRefundFee() && data.getRefundFee()>0){
paramters.put("refund_fee",data.getRefundFee());
}
}
return paramters;
}
}

微信工具类 WeChatUtils.java

public class WeChatUtils {

/**
* 根据code获取微信授权access_token
* @param request
*/
public static AuthToken getTokenByAuthCode(String code){
AuthToken authToken;
StringBuilder json = new StringBuilder();
try {
URL url = new URL(WeChatConstant.GET_AUTHTOKEN_URL+"appid="+ WeChatConstant.APP_ID+"&secret="+ WeChatConstant.APP_SECRET+"&code="+code+"&grant_type=authorization_code");
URLConnection uc = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
String inputLine ;
while((inputLine=in.readLine())!=null){
json.append(inputLine);
}
in.close();
//将json字符串转成javaBean
ObjectMapper om = new ObjectMapper();
authToken = readValue(json.toString(),AuthToken.class);
} catch (Exception e) {
throw new ServiceException("微信工具类:根据授权code获取access_token异常",e);
}
return authToken;
}

/**
* 获取微信签名
* @param map 请求参数集合
* @return 微信请求签名串
*/
public static String getSign(SortedMap<String,Object> map){
StringBuffer sb = new StringBuffer();
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
//参数中sign、key不参与签名加密
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + WeChatPayConfig.KEY);
String sign = MD5.MD5Encode(sb.toString()).toUpperCase();
return sign;
}

/**
* 解析微信服务器发来的请求
* @param inputStream
* @return 微信返回的参数集合
*/
public static SortedMap<String,Object> parseXml(InputStream inputStream) {
SortedMap<String,Object> map = Maps.newTreeMap();
try {
//获取request输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
//得到xml根元素
Element root = document.getRootElement();
//得到根元素所有节点
List<Element> elementList = root.elements();
//遍历所有子节点
for (Element element:elementList){
map.put(element.getName(),element.getText());
}
//释放资源
inputStream.close();
} catch (Exception e) {
throw new ServiceException("微信工具类:解析xml异常",e);
}
return map;
}

/**
* 扩展xstream,使其支持name带有"_"的节点
*/
public static XStream xStream = new XStream(new DomDriver("UTF-8",new XmlFriendlyNameCoder("-_","_")));

/**
* 请求参数转换成xml
* @param data
* @return xml字符串
*/
public static String sendDataToXml(WxPaySendData data){
xStream.autodetectAnnotations(true);
xStream.alias("xml", WxPaySendData.class);
return xStream.toXML(data);
}

/**
*  获取当前时间戳
* @return 当前时间戳字符串
*/
public static String getTimeStamp(){
return String.valueOf(System.currentTimeMillis()/1000);
}

/**
* 获取指定长度的随机字符串
* @param length
* @return 随机字符串
*/
public static String getRandomStr(int length){
String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
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();
}
}

微信常量类 WeChatConstant.java

public class WeChatConstant {
/**Token*/
public static final String TOKEN = "";
/**EncodingAESKey*/
public static final String AES_KEY = "";
/**消息类型:文本消息*/
public static final String MESSAGE_TYPE_TEXT = "text";
/**消息类型:音乐*/
public static final String MESSAGE_TYPE_MUSIC = "music";
/**消息类型:图文*/
public static final String MESSAGE_TYPE_NEWS = "news";
/**消息类型:图片*/
public static final String MESSAGE_TYPE_IMAGE = "image";
/**消息类型:视频*/
public static final String MESSAGE_TYPE_VIDEO = "video";
/**消息类型:小视频*/
public static final String MESSAGE_TYPE_SHORTVIDEO = "shortvideo";
/**消息类型:链接*/
public static final String MESSAGE_TYPE_LINK = "link";
/**消息类型:地理位置*/
public static final String MESSAGE_TYPE_LOCATION = "location";
/**消息类型:音频*/
public static final String MESSAGE_TYPE_VOICE = "voice";
/**消息类型:事件推送*/
public static final String MESSAGE_TYPE_EVENT = "event";
/**事件类型:subscribe(订阅)*/
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
/**事件类型:unsubscribe(取消订阅)*/
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
/**事件类型:CLICK(自定义菜单点击事件)*/
public static final String EVENT_TYPE_CLICK = "CLICK";
/**返回消息类型:转发客服*/
public static final String TRANSFER_CUSTOMER_SERVICE="transfer_customer_service";
/**ACCESS_TOKEN*/
public static final String ACCESS_TOKEN_ENAME = "access_token";
/**返回成功字符串*/
public static final String RETURN_SUCCESS = "SUCCESS";
/**主动发送消息url*/
public static final String SEND_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=";
/**通过code获取授权access_token的URL*/
public static final String GET_AUTHTOKEN_URL = " https://api.weixin.qq.com/sns/oauth2/access_token?"; }

其他微信对象:

封装微信授权返回的信息,此处属性均为小写【微信返回的是小写很不友好】

public class AuthToken implements Serializable {
/**授权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;
/**getter() and setter()*/
}

微信请求参数对象【下单与退款均可使用此对象】

public class WxPaySendData {

/**公众账号ID 必须*/
@XStreamAlias("appid")
private String appId;

/**商户号 必须*/
@XStreamAlias("mch_id")
private String mchId;

/**设备号*/
@XStreamAlias("device_info")
private String deviceInfo;

/**随机字符串 必须*/
@XStreamAlias("nonce_str")
private String nonceStr;

/**签名 必须*/
@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 outTradeNo;

/**货币类型*/
@XStreamAlias("fee_type")
private String feeType;

/**交易金额 必须[JSAPI,NATIVE,APP]*/
@XStreamAlias("total_fee")
private int totalFee;

/**交易类型 [必须]*/
@XStreamAlias("trade_type")
private String tradeType;

/**通知地址 [必须]*/
@XStreamAlias("notify_url")
private String notifyUrl;

/**终端Ip [必须]*/
@XStreamAlias("spbill_create_ip")
private String spBillCreateIp;

/**订单生成时间yyyyMMddHHmmss*/
@XStreamAlias("time_start")
private String timeStart;

/**订单失效时间yyyyMMddHHmmss 间隔>5min*/
@XStreamAlias("time_expire")
private String timeExpire;

/**用户标识 tradeType=JSAPI时必须*/
@XStreamAlias("openid")
private String openId;

/**商品标记*/
@XStreamAlias("goods_tag")
private String goodsTag;

/**商品ID tradeType=NATIVE时必须*/
@XStreamAlias("product_id")
private String productId;

/**指定支付方式*/
@XStreamAlias("limit_pay")
private String limitPay;

/**
*以下属性为申请退款参数
*/
/**微信订单号 [商户订单号二选一]*/
@XStreamAlias("transaction_id")
private String transactionId;

/**商户退款单号 [必须]*/
@XStreamAlias("out_refund_no")
private String outRefundNo;

/**退款金额 [必须]*/
@XStreamAlias("refund_fee")
private Integer refundFee;

/**货币种类*/
@XStreamAlias("refund_fee_type")
private String refundFeeType;

/**操作员账号:默认为商户号 [必须]*/
@XStreamAlias("op_user_id")
private String opUserId;

/**getter() and setter()*/
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息