# 操作流程

  1. 注册微信公众号、微信支付商户号,并做好基础配置(不解释配置详情,无非是获取 appid,商户号等)
  2. 微信支付接口代码
  3. 微信支付回调接口代码
  4. 微信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可查看http://blog.hce-space.top/blog/6; (3):获得微信支付所需的配置文件,

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

#微信对接配置
weixin:
  id: 
  appId: **************
  secret: ***********
  mchId: ************
  mchKey: *************
  redirect-url: http://www.dadsa.com/
  refund-notify-url: http://www.api.dadsa.com/pay/notify/refund
  pay-notify-url: http://www.api.dadsa.com/pay/notify/pay
  auth-file: MP_verify_TxToYqnU86bNRnVu.txt
  expires-time: 
  key-path: classpath:/weixin/apiclient_cert.p12

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

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

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

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

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

    微信支付的路径配置,这就是支付授权目录,注意这里是目录,不是仅仅是域名,后面要加你要掉起支付的html页面的最后一个路径要加/。如果不加掉用起不来支付界面。

2. 撸代码,Java搞起来

2.1:先把微信配置文件写入一个java类:

@Data
@ConfigurationProperties(prefix = "weixin")
public class WxPayProperties {
  /**
   * 设置微信公众号或者小程序等的appid
   */
  private String appId;

  /**
   * 微信支付商户号
   */
  private String mchId;

  /**
   * 微信支付商户密钥
   */
  private String mchKey;
  /**
   * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除
   */
  private String subAppId;

  /**
   * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除
   */
  private String subMchId;

  /**
   * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定
   */
  private String keyPath;

    private String secret;
    private String id;
    private String aesKey;
    private Long expiresTime;
    private String redirectUrl;
    private String refundNotifyUrl;
    private String payNotifyUrl;
    private String authFile;


  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this,
        ToStringStyle.MULTI_LINE_STYLE);
  }
}

2.2 配置微信支付bean,这一步使用Binary Wang的jar包后配置十分简单:

@Configuration
public class WxConfig {
 private final WxPayProperties wxPayProperties;
 private final CustomWxMpInRedisConfigStorage storage;

 @Autowired
 public WxConfig(WxPayProperties wxPayProperties, CustomWxMpInRedisConfigStorage storage) {
     this.wxPayProperties = wxPayProperties;
     this.storage = storage;
 }

 @Bean
 public WxMpService wxMpService() {
     WxMpService service = new WxMpServiceImpl();
     storage.setAppId(wxPayProperties.getAppId());
     storage.setSecret(wxPayProperties.getSecret());
     storage.setOauth2redirectUri(wxPayProperties.getRedirectUrl());
     storage.setExpiresTime(wxPayProperties.getExpiresTime());
     service.setWxMpConfigStorage(storage);
     return service;
 }

 @Bean
 public WxPayConfig wxPayConfig() {
     WxPayConfig payConfig = new WxPayConfig();
     payConfig.setAppId(wxPayProperties.getAppId());
     payConfig.setMchId(wxPayProperties.getMchId());
     payConfig.setMchKey(wxPayProperties.getMchKey());
     payConfig.setNotifyUrl(wxPayProperties.getRedirectUrl());
     payConfig.setKeyPath(wxPayProperties.getKeyPath());
     payConfig.setTradeType("JSAPI");
     payConfig.setSignType("MD5");
     return payConfig;
 }

 @Bean
 public WxPayService wxPayService(WxPayConfig payConfig) {
     WxPayService wxPayService = new WxPayServiceImpl();
     wxPayService.setConfig(payConfig);
     return wxPayService;
 }
}

2.3 微信支付控制器,这是我之前写的系统:

/**
 * 微信对接控制器,微信支付
 */
@Controller
@RequestMapping("/pay")
public class WxPayController extends BaseController {
	private final Logger logger = LoggerFactory.getLogger("WxPayController");
	 
	 @Autowired
    private WxMpService service;
    @Autowired
    private WxPayProperties wxPayProperties;
    @Autowired
    private WXService ourWxService;
	 
	    /**
     * 统一下单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
     * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
     * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
     * request请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
     *
     * @param orderNumber 这里是我自己系统的订单号,可以根据这个查出来需要的费用等等
     */
    @PostMapping("/unifiedOrder/{orderNumber}")
    @MapTo(WxPayDto.class)
    public WxPayMpOrderResult unifiedOrder(@PathVariable String orderNumber, Principal principal, HttpServletRequest req) {
        String wxOpenid = getUser(principal).getWxOpenid();
        return ourWxService.unifiedOrder(orderNumber, wxOpenid, principal, req);
    }
	
	
    /**
     * 付款微信回调
     *
     * @param body 微信返回参数
     * @return
     */
    @RequestMapping("notify/pay")
    public Object payNotify(@RequestBody String body) {
        return ourWxService.payNotify(body);
    }
	
	/**
     * <pre>
     * 微信支付-申请退款
     * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
     * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund
     * </pre>
     * <p>
     * request 请求对象
     *
     * @return 退款操作结果
     */
    @PostMapping("/refund/{orderType}/{id}")
    public WxPayRefundResult refund(@PathVariable OrderType orderType, @PathVariable String id) throws WxPayException {
        OrderInfo order = orderService.getByType((short) orderType.origin(), id);
		//组装退款对象
        WxPayRefundRequest request = new WxPayRefundRequest();
        request.setOutTradeNo(order.getNumber());
        request.setOutRefundNo(order.getNumber());
        request.setTotalFee((int) (order.getPrice() * 100));
        request.setRefundFee((int) (order.getPrice() * 100));
        request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
        return this.wxService.refund(request);
    }
	
	/**
     * 退款结果通知
     *
     * @param request
     * @throws WxPayException
     */
    @RequestMapping("/notify/refund")
    public Object reFundNotify(HttpServletRequest request) {
        return ourWxService.reFundNotify(request);
    }

}

Service:

    @Override
    public WxPayMpOrderResult unifiedOrder(String orderNumber, String wxOpenid, Principal principal, HttpServletRequest req) {
           OrderInfo order = orderService.getOrder(orderNumber);
           String lable = "测试付款";
			//组装前端所需付款参数
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            String ip = req.getRemoteAddr();
            request.setOutTradeNo(orderNumber);
			//微信支付都以分为单位,此处*100
            request.setTotalFee((int) (order.getPrice() * 100));
            request.setBody(order.getType().label() + "-" + lable);
            request.setTradeType(TRADETYPE);
            request.setNotifyUrl(wxPayProperties.getPayNotifyUrl());
            request.setSpbillCreateIp(ip);
            request.setOpenid(wxOpenid);
            try {
                return wxService.createOrder(request);
            } catch (WxPayException e) {
                e.printStackTrace();
            }
    }

    @Override
    public Object payNotify(String body) {
        WxPayOrderNotifyResult result = null;
        try {
            result = wxPayService.parseOrderNotifyResult(body);
            if (SUCCESS_CODE.equals(result.getReturnCode())) {
			//返回微信接收成功,否则微信会一直调用此接口
                 WxPayNotifyResponse response = new WxPayNotifyResponse();
                 response.setReturnCode("SUCCESS");
                 response.setReturnMsg("Ok");
                 return response;
            }
            return null;
        } catch (WxPayException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Object reFundNotify(HttpServletRequest request) {
        logger.error("接收到退款通知");
        String body = readRequestStr(request);
        WxPayRefundNotifyResult wxPayRefundNotifyResult = null;
        try {
            wxPayRefundNotifyResult = wxPayService.parseRefundNotifyResult(body);
        } catch (WxPayException e) {
            e.printStackTrace();
        }
        WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();
        if (SUCCESS_CODE.equals(wxPayRefundNotifyResult.getReturnCode())) {
		//返回微信接收成功,否则微信会一直调用此接口
           WxPayNotifyResponse response = new WxPayNotifyResponse();
           response.setReturnCode("SUCCESS");
           response.setReturnMsg("Ok");
           return response;
        }
        return null;
    }

2.3 前端代码:

<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+"/pay/unifiedOrder/124534",
    		   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" οnclick="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方法,发起支付——》支付结果同步返回结果——》支付结果异步发送到后台回调控制器做结果处理