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

Java微信支付全教程demo【公众号支付】

2017-12-01 15:53 549 查看
直入主题:

注册微信公众号、微信支付商户号,并做好基础配置(不解释配置详情,无非是获取 appid,商户号等)
微信支付接口代码
微信支付回调接口代码
微信h5支付页面唤起字符密码界面完成支付   

1,写代码之前准备工作

(1):利用开源代码 weixin-java-tools来开发效率很高,免去了很多繁琐的代码开发量;

链接 https://github.com/wechat-group/weixin-java-tools

搭建maven工程,引入:

<!-- 微信支付 开始-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>2.8.0</version>
<classifier>sources</classifier>
</dependency>

<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.8.0</version>
</dependency>

<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.8.0</version>
<classifier>sources</classifier>
</dependency>

<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-common</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-common</artifactId>
<version>2.8.0</version>
<classifier>sources</classifier>
</dependency>

<!-- 微信支付 结束 -->


(2):微信支付开发接口需要 用到用户openId参数,至于微信授权获取用户openId这里不做解释;

(3):获得微信支付所要的配置文件,

这里我配置的有参数的都是必须要填写的,其他的可以不写,这里WX_WEB_URL这个是你网站的网址在回调的时候需要用到,我把这个地址配置到了配置文件里了。

#微信对接配置
WX_APPID=wx11231231231
WX_APPSECRET=321321321321321321
WX_TOKEN=
WX_AESKEY=
#微信支付商户号
WX_mchId=432432432
#微信支付平台商户API密钥
WX_mchKey=fgfdfdewrewrwer432432
#服务商模式下的子商户公众账号ID
WX_subAppId=
#服务商模式下的子商户号
WX_subMchId=
WX_keyPath=
WX_WEB_URL=http://7cvyn3.natappfree.cc/zc

(4):这个是我们从微信平台上获取的配置文件,还有两个重要的授权地址是我们要在微信平台上配置的,这个也是微信极为坑的一点

1:在微信公众平台——》权限接口——》网页授权获取用户基本信息

网址: https://mp.weixin.qq.com  微信公众号登录入口

必须填入外网域名并且要下载提示里的.txt文件,放到你网站的跟目录下,可以通过网站直接访问的路径

推荐一个内网穿透工具,调试微信开发非常方便https://natapp.cn/#download

 

这一步配置好后,还有一步就是微信支付的路径配置,这里最坑,我已在崩溃的边缘,这就是支付授权目录,注意这里是目录,不是仅仅是域名,后面要加你要掉起支付的html页面的最后一个路径要加/。当初我只写了域名,然后怎么都掉用起不来支付界面。坑了我好几天,

到这一步,如果都没有问,你基础的配置就完成了,下面就到了撸代码的步骤了,由于我们用的是开源工具,代码量其实非常简单,只需要一点配置就好。

2:撸代码,Java搞起来

2.1:先把微信配置文件 通过spring写到bean中;

<!-- 微信基础配置 -->
<bean name="wxConfig" class="me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage">
<property name="appId" value="${WX_APPID}" />
<property name="secret" value="${WX_APPSECRET}" />
</bean>
<!-- 微信核心service注入 -->
<bean id="wxMpService" class="me.chanjar.weixin.mp.api.impl.WxMpServiceImpl">
<property name="wxMpConfigStorage" ref="wxConfig" />
</bean>

<!-- 微信支付配置 -->
<bean name="wxPayConfig" class="com.github.binarywang.wxpay.config.WxPayConfig">
<property name="appId" value="${WX_APPID}" />
<property name="mchId" value="${WX_mchId}" />
<property name="mchKey" value="${WX_mchKey}" />
<property name="subAppId" value="${WX_subAppId}" />
<property name="subMchId" value="${WX_subMchId}" />
<property name="keyPath" value="${WX_keyPath}" />
</bean>
<!-- 微信支付service注入 -->
<bean id="wxPayService" class="com.github.binarywang.wxpay.service.impl.WxPayServiceImpl">
<property name="config" ref="wxPayConfig" />
</bean>


2.2:直接进入重点,微信支付控制器,微信支付欲支付接口和回调接口:相关工具类封装在下面。

支付那我已经把业务和微信支付做了分离。

/**
* 微信对接控制器,微信支付
* Project Name:zc_app_api
* File Name:WxInitController.java
* Package Name:com.zc.app.api.controller.weixin
* Date:2017年9月30日下午3:10:19
* @author 吉文剑
*/
@Controller
@RequestMapping("/wxPay/")
public class WxPayController extends BaseController {
private final Logger logger = LoggerFactory.getLogger("WxPayController");

@Autowired
private WxPayConfig payConfig;
@Autowired
private WxPayService payService;

/**
*微信公众号支付,业务方法
* @param response
* @param request
*/
@RequestMapping(value = "toPayInfo")
public void getJSSDKPayInfo(HttpServletResponse response,
HttpServletRequest request) {
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");

String orderSubject = "停车缴费0.01元";//商品描述
String merchantTradeNo = df.format(new Date());//商户订单号
Integer totalAmount = 1;//订单总金额,单位为分
String goodsDesc = "万达停车场下午1点-9点";
String gooodsCode = "code"+merchantTradeNo;

Map<String, Object> payData = getPayData( merchantTradeNo,
orderSubject,
totalAmount,
goodsDesc,
gooodsCode);

resultPayData(response,payData);
return;
}

/** 微信公众号支付接口,通过参数生成网页微信js支付参数,掉起支付界面必须参数
* @param merchantTradeNo		商户订单号(必填)
* @param orderSubject			订单名称(必填)
* @param totalAmount			订单金额,单位分(必填)
* @param goodsDesc				商品描述(可空)
* @param gooodsCode			商品编码(可空)
* Date:2017年12月4日上午11:04:04
* @author 吉文剑
*/
private Map<String, Object> getPayData(String merchantTradeNo,
String orderSubject,
Integer totalAmount,
String goodsDesc,
String gooodsCode){
Map<String, Object> map = new HashMap<String, Object>();
ConstantUtils instance = ConstantUtils.getInstance();
ZuUser sessUser = (ZuUser) session.getAttribute(BaseController.SESSION_USER);
if(null == sessUser || !StringUtils.isValid(sessUser.getOpenId())){
map.put("result", false);
return map;
}
WxPayUnifiedOrderRequest prepayInfo = WxPayUnifiedOrderRequest.newBuilder()
.openid(sessUser.getOpenId())
.outTradeNo(merchantTradeNo)
.totalFee(totalAmount)
.body(orderSubject)
.tradeType(WeixinUtils.TRADE_TYPE)
.spbillCreateIp(request.getRemoteAddr())
.notifyURL(instance.getPropertyValue("WEB_URL")+WeixinUtils.NOTIFY_URL)
.nonceStr(WeixinUtils.getNonceStr())
.detail(goodsDesc)
.productId(gooodsCode)
.build();
try {
Map<String, String> payInfo = this.payService.getPayInfo(prepayInfo);
map.put("result", true);
map.put("data", payInfo);
} catch (WxPayException e) {
map.put("result", false);
map.put("data", e.getErrCodeDes());
this.logger.error(e.getErrCodeDes());
e.printStackTrace();
}
return map;
}

/**
* 微信通知支付结果的回调地址,notifyCallback
*
* @param request
* @param response
*/
@RequestMapping(value = "notifyCallback")
public void notifyCallback(HttpServletRequest request,  HttpServletResponse response) {
try {
synchronized (this) {
Map<String, String> kvm = XMLUtil.parseRequestXmlToMap(request);
String orderCode = null;//回调 支付订单号
String resultCode = null;//回调支付是否成功状态吗
String totalFee = null;//支付金额
System.out.println("微信支付回调参数:");
System.out.println(kvm);
if (SignUtils.checkSign(kvm, this.payConfig.getMchKey())) {
orderCode =  kvm.get("out_trade_no");
resultCode = kvm.get("result_code");
totalFee = kvm.get("total_fee");
if ("SUCCESS".equals(resultCode)) {
//TODO(user) 微信服务器通知此回调接口支付成功后,通知给业务系统做处理
logger.info("out_trade_no: " + orderCode + " pay SUCCESS!");
response.getWriter().write(WeixinUtils.WX_PAY_SUCCESS);
} else {
this.logger.error("out_trade_no: "  + orderCode + " result_code is FAIL");
response.getWriter().write(WeixinUtils.WX_PAY_FAIL);
}
} else {
this.logger.error("out_trade_no: " + orderCode + " check signature FAIL");
response.getWriter().write(WeixinUtils.WX_PAY_SIGN_FAIL);
}

if("SUCCESS".equals(resultCode)){
//支付成功的业务逻辑
//totalFee   要判断支付金额是否等于订单金额!!!
System.out.println("支付成功:订单号:"+orderCode+",支付金额:"+totalFee);

}else{
//支付失败的业务逻辑
System.out.println("微信支付 回调 :*-************支付失败");
}

}
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 客户端返回JSON字符串
* @param response
* @param object
* @return
*/
protected String resultPayData(HttpServletResponse response, Object object) {
try {
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
//解决跨域问题
response.setHeader("Access-Control-Allow-Origin", "*");
response.getWriter().print(new Gson().toJson(object));
return null;
} catch (IOException e) {
return null;
}
}

}


2.3:自己整理了一个工具类,你们后面会用的到的,基本都是写配置项,不会变的:

/**
* 微信通用工具类
* Project Name:zc_app_api
* File Name:WeixinUtils.java
* Package Name:com.zc.app.api.utils
* Date:2017年10月18日下午1:45:01
* @author 吉文剑
*/
public class WeixinUtils {
/** 微信支付回调支付成功,返回成功结果 */
public static final String WX_PAY_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[ok]]></return_msg></xml>";
/** 微信支付回调支付失败,返回失败结果 */
public static final String WX_PAY_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[result_code is FAIL]]></return_msg></xml>";
/** 微信支付回调支付sign验证失败,返回sign验证失败结果 */
public static final String WX_PAY_SIGN_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[check signature FAIL]]></return_msg></xml>";
/** 微信支付回调地址路径 */
public static final String NOTIFY_URL = "/wxPay/notifyCallback.do";
/** 微信获取微信用户授权后用户信息 地址路径 */
public static final String OAUTH2_USERINFO_URL = "/wx/getOAuth2UserInfo.do";
/** 微信官方api接口 */
public static final String URL_OAUTH2 = "https://open.weixin.qq.com/connect/oauth2/authorize?";
/** 获取微信用户信息 */
public static final String SCOPE = "snsapi_userinfo";
/** 交易类型 :jsapi代表微信公众号支付 */
public static final String TRADE_TYPE = "JSAPI";

/** 获取微信openId URL  */
public String getWxOpenIdUrl(String toUrl){
ConstantUtils instance = ConstantUtils.getInstance();
StringBuffer url = new StringBuffer();
url.append(URL_OAUTH2)
.append("appid=").append(instance.getPropertyValue("WX_APPID"))
.append("&redirect_uri=").append(instance.getPropertyValue("WX_WEB_URL") + OAUTH2_USERINFO_URL)
.append("&response_type=code")
.append("&scope=").append(SCOPE)
.append("&state=").append(toUrl)
.append("#wechat_redirect");
return url.toString();
}

/**
* 获得微信支付随机码
* @return
* Date:2017年12月4日上午9:50:48
* @author 吉文剑
*/
public static String getNonceStr(){
return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
}

}


2.4:其他相关工具类:

/**
* @ClassName: ConstantUtils
* @Description: (读取配置文件的信息,一些公共的属性,参数配置在)
* @author JiWenJian
*/
public class ConstantUtils {
private static final String FILEPATH = "/zc_app_api.properties";

private static ConstantUtils instance;

private ConstantUtils(){
}

public static ConstantUtils getInstance(){
if(null == instance ){
instance  = new ConstantUtils();
}
return instance;
}

/**
* @Description: (读取文件信息)
* @author JiWenJian
* @date 2012-11-22 下午01:42:08
* @param key
* @return
*/
public String getPropertyValue(String key) {
Properties props = new Properties();
try {
InputStream in = getClass().getResourceAsStream(FILEPATH);
props.load(in);
return props.getProperty(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* @Description: (读取配置Integer数值信息)
* @author JiWenJian
* @date 2012-11-22 下午01:42:08
* @param key
* @return
*/
public Integer getPropertyIntegerValue(String key) {
Properties props = new Properties();
try {
InputStream in = getClass().getResourceAsStream(FILEPATH);
props.load(in);
return Integer.parseInt(props.getProperty(key));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

}


/**
*  xml解析成map对象
*/
public class XMLUtil {

/**
* 将微信服务器发送的Request请求中Body的XML解析为Map
*
* @param request
* @return
* @throws Exception
*/
public static Map<String, String> parseRequestXmlToMap(HttpServletRequest request) throws Exception {
// 解析结果存储在HashMap中
Map<String, String> resultMap;
InputStream inputStream = request.getInputStream();
resultMap = parseInputStreamToMap(inputStream);
return resultMap;
}

/**
* 将输入流中的XML解析为Map
*
* @param inputStream
* @return
* @throws DocumentException
* @throws IOException
*/
public static Map<String, String> parseInputStreamToMap(InputStream inputStream) throws DocumentException, IOException {
// 解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
//得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
//遍历所有子节点
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
//释放资源
inputStream.close();
return map;
}

/**
* 将String类型的XML解析为Map
*
* @param str
* @return
* @throws Exception
*/
public static Map<String, String> parseXmlStringToMap(String str) throws Exception {
Map<String, String> resultMap;
InputStream inputStream = new ByteArrayInputStream(str.getBytes("UTF-8"));
resultMap = parseInputStreamToMap(inputStream);
return resultMap;
}

}


3:下面到了前台html页面,只剩下最后一小步了,只要掉起微信支付密码界面就大功告成了;

<head>

<script type="text/javascript" src="http://g.alicdn.com/sj/lib/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script src="../js/common.js"></script>
<script type="text/javascript">

function toPay(){
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();
}
}

function onBridgeReady(){
$.ajax({
type: "POST",
url: DEF_GLOBAL_DOMAIN+"/wxPay/toPayInfo.do",
success: function(data){
console.log(data);

WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : data.data.appId,
"timeStamp": data.data.timeStamp,
"nonceStr" : data.data.nonceStr,
"package" : data.data.package,
"signType" :  data.data.signType,
"paySign" : data.data.paySign
},function(res){
$("#msgId").html(res.err_msg);
if(res.err_msg == "get_brand_wcpay_request:ok"){
$("#resId").html("支付成功");
//	location.href="weixinPayResult.html";//支付成功跳转到指定页面
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
$("#resId").html("支付取消");
}else{
$("#resId").html("支付失败");
}
});
}
});

}

</script>
</head>
<body>

<div class="content">
<div class="form-area">
<div class="inp">
支付0.01元
</div>
<button class="em-submit-st2" onclick="toPay()" >
确定支付
</button>
</div>
结果:
<p/>
<div id="resId"></div>
参数:
<p/>
<div id="invokeId"></div>
<br/><p/>
返回:
<p/>
<div id="msgId"></div>
</div>

</body>
</html>


到这里整个微信支付就完成了,总结:难点在于微信平台繁琐的配置,很繁琐,错一个整个都跑不通,还以为是自己代码有问题,坑爹。代码层,感觉没有什么复杂量,就做一些配置就好了,至于微信老是喜欢用各种 签名    sign  

这个工具已经给我们解决了,只要各个地方的配置没有问题,其实就很简单了。

整个执行流程 是 :

微信点击支付按钮——》

       发送ajax到支付请求控制器——》

       返回支付参数——》

       用支付参数,调用微信内嵌的掉起支付js方法,发起支付——》

      支付结果同步返回结果——》

      支付结果异步发送到后台回调控制器做结果处理

我在本文章中只做了代码层和简单的讲解,至于整个微信支付的细节,可以访问下面的作者:写的比较详细:https://www.cnblogs.com/yimiyan/p/5603657.html

感谢同是天涯代码人,花这么长时间来参读本文章,加油把。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息