1. 程式人生 > >公眾號微信支付開發

公眾號微信支付開發

The 部署 func else ldr 發包 ati 處理 fig

公眾號微信支付開發

1.第一步:設置微信支付目錄,這個地址指到支付頁面的上一級即可。

例如:支付頁面的地址是http://www.baidu.com/wechat/pay/shopping,只需填寫http://www.baidu.com/wechat/pay/,

一定要以"/"(左斜桿)符號結尾。

技術分享圖片

2.第二步:設置授權域名,授權域名是為了獲取支付中不可缺少的參數openid。每個用戶對於每個公眾號的openid都是不同的且是唯一的,即是說一個用戶在不同的公眾號中,他的openid是不同的,並且一直不變。在開發中可以事先獲取你自己的在這個公眾號(正式公眾號,具有支付權限的)的openid,然後就可以跳過授權過程,直接開發並測試支付功能。

技術分享圖片

3.第三步:引入微信開發jar包,這是別人已經封裝好的微信支付API,當然也可以使用官方的微信支付SDK,不過為了方便快速開發,

所以這裏我使用了封裝好的別人封裝好的API,這是API文檔的github地址:https://github.com/wechat-group/weixin-java-tools/wiki ,裏面有具體的使用方法和開發步驟,如果你嫌看文檔麻煩的話可以直接看我的開發步驟:

這是基於springboot開發的,首先引入jar包:

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
            <version>3.1.4.BETA</version>
        </dependency>    

4.加入微信開發包兩個基本類:

(1)在springboot配置文件application.properties中加入微信支付基本參數:

#微信公眾號或者小程序等的appid
wechatpay.appId =

#微信支付商戶號
wechatpay.mchId =

#微信支付商戶密鑰
wechatpay.mchKey=

#服務商模式下的子商戶公眾賬號ID,用不上就註釋掉
#wechatpay.subAppId=

#服務商模式下的子商戶號,用不上就註釋掉
#wechatpay.subMchId=

# p12證書的位置,可以指定絕對路徑,也可以指定類路徑(以classpath:開頭)用不上就註釋掉
#wechatpay.keyPath
=

(2)添加支付配置文件參數類WxPayProperties:

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * wxpay pay properties
 *
 * @author Binary Wang
 */
@ConfigurationProperties(prefix = "wechatpay")
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;

    public String getAppId() {
        return this.appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getMchId() {
        return mchId;
    }

    public void setMchId(String mchId) {
        this.mchId = mchId;
    }

    public String getMchKey() {
        return mchKey;
    }

    public void setMchKey(String mchKey) {
        this.mchKey = mchKey;
    }

    public String getSubAppId() {
        return subAppId;
    }

    public void setSubAppId(String subAppId) {
        this.subAppId = subAppId;
    }

    public String getSubMchId() {
        return subMchId;
    }

    public void setSubMchId(String subMchId) {
        this.subMchId = subMchId;
    }

    public String getKeyPath() {
        return this.keyPath;
    }

    public void setKeyPath(String keyPath) {
        this.keyPath = keyPath;
    }

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

(3)初始化微信支付配置類WxPayConfiguration

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;

/**
 * @author Binary Wang
 */
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
public class WxPayConfiguration {
    private WxPayProperties properties;

    @Autowired
    public WxPayConfiguration(WxPayProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean
    public WxPayService wxService() {
        WxPayConfig payConfig = new WxPayConfig();
        
        payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
        payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
        payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
        payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
        payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));

        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

}

5.生成支付訂單:

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.park.pojo.park.ParkingRecord;
import com.park.service.Interface.ParkService;
import com.park.utils.parkInterfaceUtil.ParkInterfaceRequestParam;
import com.park.utils.parkInterfaceUtil.ParkProtocolParamUtil;
import com.park.utils.wechatUtil.ConstantUtil;
import com.park.utils.wechatUtil.HttpRequest;
import com.park.utils.wechatUtil.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付接口
 */
@Slf4j
@RestController
@RequestMapping(value = "/wechat/pay")
public class WechatPayController {

    @Autowired
    private WxPayService wxService;

    /**
     * 統一下單
     * 在發起微信支付前,需要調用統一下單接口,返回微信支付的所需要的參數
     * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
     *
     * @param request 請求對象,註意一些參數如appid、mchid等不用設置,方法內會自動從配置對象中獲取到(前提是對應配置中已經設置)
     */
    @PostMapping("/getPayInfo")
    public WxPayMpOrderResult getPayInfo(@RequestBody WxPayUnifiedOrderRequest request , HttpServletRequest httpServletRequest) throws WxPayException,Exception {

        request.setOutTradeNo(SignUtil.buildRandom(10));                                         //隨機字訂單號
        request.setSpbillCreateIp(ConstantUtil.getRemortIP(httpServletRequest));                    //用戶ip
        request.setTradeType("JSAPI");                                                              //公眾號支付
        request.setNonceStr(SignUtil.createNonceStr());                                             //隨機字符串
        request.setNotifyUrl("http://qwrerewrwqewq/wechat/pay/PayResultNotify");                //回調通知支付結果地址(必須外網能訪問的地址)
        request.setDeviceInfo("WEB");                                                               //客戶終端類型
        request.setSignType("MD5");                                                                 //加密方式(必須參數,雖然官方文檔說是非必需,親測不加一直報簽名錯誤)

        return this.wxService.createOrder(request);
    }

    /**
     * 接受微信支付返回通知
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping("/PayResultNotify")
    public void PayResultNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        log.info("微信支付返回通知函數開始---------------------");

        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        String result = new String(outSteam.toByteArray(), "utf-8");
        boolean isPayOk =false;
        WxPayOrderNotifyResult wxPayOrderNotifyResult =null;

        // 此處調用訂單查詢接口驗證是否交易成功
        try {
            wxPayOrderNotifyResult = wxService.parseOrderNotifyResult(result);
            if("SUCCESS".equals(wxPayOrderNotifyResult.getResultCode())){
                isPayOk=true;
            }
            log.info("解析數據:"+wxPayOrderNotifyResult.toString());
        } catch (WxPayException e) {
            e.printStackTrace();
        }

        String noticeStr="";


        // 支付成功,商戶處理後同步返回給微信參數
        PrintWriter writer = response.getWriter();
        if (isPayOk) {
          //建議在這裏處理付款完成的業務(雖然前端也可以處理後續業務,但是前端處理並不安全,例如:客戶突然關閉瀏覽器了等情況,付款成功後續的業務將中斷)
                System.out.println("===============付款成功,業務處理完畢==============");
                // 通知微信已經收到消息,不要再給我發消息了,否則微信會8連擊調用本接口
                noticeStr = setXML("SUCCESS", "OK");
                log.info("收到通知返回給微信api信息:-----------"+noticeStr);
                writer.write(noticeStr);
                writer.flush();
            
        } else {

            // 支付失敗, 記錄流水失敗
            noticeStr = setXML("FAIL", "");
            writer.write(noticeStr);
            writer.flush();
            System.out.println("===============支付失敗==============");
        }



    }

    public static String setXML(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }
}

6.所用到的工具類:

import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;

public class SignUtil {
    private static String token = "weixin";

    /**
     * 獲取acassToken校驗
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        boolean result = false;

        // 對token、timestamp和nonce按字典序排序
        String[] array = new String[]{token, timestamp, nonce};
        Arrays.sort(array);

        // 將三個參數字符拼接成一個字符串
        String str = array[0].concat(array[1]).concat(array[2]);

        String sha1Str = null;
        try {
            // 對拼接後的字符串進行sha1加密
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(str.getBytes());
            sha1Str = byte2str(digest);
        }
        catch(Exception e) {
        }

        if(sha1Str != null &&  sha1Str.equals(signature)) {
            result = true;
        }

        return result;
    }

    /*
     * 將字節數組轉換成字符串
     */
    public static String byte2str(byte[] array) {
        StringBuffer hexstr = new StringBuffer();
        String shaHex="";
        for(int i = 0; i < array.length; i++) {
            shaHex = Integer.toHexString(array[i] & 0xFF);
            if(shaHex.length() < 2) {
                hexstr.append(0);
            }
            hexstr.append(shaHex);
        }
        return hexstr.toString();
    }

    /**
     * 獲取acaccessToken
     * @param grant_type
     * @param appid
     * @param secret
     * @return
     */
    public static String getAcaccessToken(String grant_type,String appid,String secret){

        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+grant_type+"&appid="+appid+"&secret="+secret;
        String Str = HttpRequest.httpRequestGet(url);
        return Str;
    }

    /**
     * 創建隨機字符串
     * @return
     */
    public static String createNonceStr() {
        return UUID.randomUUID().toString().substring(0, 20);
    }

    /**
     * 獲取時間戳
     * @return
     */
    public static String getCurrTime(){

        long times = new Date().getTime();
        return times+"";
    }

    /**
     * 隨機數
     * @param length
     * @return
     */
    public static String buildRandom(int length){
        long times = new Date().getTime();
        int randomNum = (int)((Math.random()*9+1)*(10*length));

        return randomNum+""+times;
    }

    /**
     * 獲取用戶IP
     * @param request
     * @return
     */
    public static String getRemortIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        String ipListStr = request.getHeader("x-forwarded-for");
        if(!(ipListStr.indexOf(",")<0)){
            String [] list = ipListStr.split(",");
            return list[0];                                 //當服務部署使用代理,其獲取到的IP地址將會是多個,取第一個
        }else {
            return ipListStr ;
        }

    }
}

7.前端代碼:

我的前端是用vue寫的,看下邏輯就可以了:

getPayInfo(params){                                              //調用統一下單接口獲取js支付參數  參數:金額、商品名稱等 具體可以看統一接口的接收參數類WxPayUnifiedOrderRequest
        this.$api.gotoPay(params).then(res=>{  
          if(res!=null){
            this.payInfo.appId = res.data.appId;
            this.payInfo.timeStamp = res.data.timeStamp;
            this.payInfo.nonceStr = res.data.nonceStr;
            this.payInfo.packages = res.data.packages;
            this.payInfo.sign = res.data.sign;
          }
        })
      },
      onBridgeReady(){                                //使用微信瀏覽器內置的對象調起微信支付插件,並傳入統一接口返回的參數
        WeixinJSBridge.invoke(
          ‘getBrandWCPayRequest‘, {
            "appId":this.payInfo.appId,                 //公眾號名稱,由商戶傳入
            "timeStamp":this.payInfo.timeStamp,         //時間戳,自1970年以來的秒數
            "nonceStr":this.payInfo.nonceStr,           //隨機字符串
            "package":this.payInfo.packages,            //支付驗證pay_id
            "signType":"MD5",                           //微信簽名方式
            "paySign":this.payInfo.sign                 //微信簽名
          },
          function(res){
            if(res.err_msg == "get_brand_wcpay_request:ok" ){
              // 使用以上方式判斷前端返回,微信團隊鄭重提示:
              //res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。
            }
          });
      }
      ,
      Gopay(){                                            //點擊付款按鈕開始支付
        console.log("開始支付")
        if (typeof WeixinJSBridge == "undefined"){
          if( document.addEventListener ){
            document.addEventListener(‘WeixinJSBridgeReady‘, this.onBridgeReady(), false);
          }else if (document.attachEvent){
            document.attachEvent(‘WeixinJSBridgeReady‘, this.onBridgeReady());
            document.attachEvent(‘onWeixinJSBridgeReady‘, this.onBridgeReady());
          }
        }else{
          this.onBridgeReady();
        }
      }

(8)測試支付功能:

可以將支付頁面的地址發送到正式的公眾號(必須要正式的公眾號才行,而且後臺支付相關的參數也要用正式,同時要用傳入的openid的那個用戶點擊才能測試,一般是本人的),然後點擊開始測試支付。



公眾號微信支付開發