SpringBoot從入門到精通教程(二十九)- 微信企業支付集成(五分鐘集成)

需求背景

SpringBoot用法:微信企業支付集成(五分鐘集成)

問題痛點

通過SpringBoot框架,集成服務端微信企業支付接口,做到下載即用(填寫好相關微信支付後臺相關Key信息),最快五分鐘集成成功,節省時間,同時也避免重複採坑。你也可能在此基礎上優化代碼,或者二次開發,希望對你有用。

  • 目前這套代碼在生產環境中運行超過一年時間,已成功給用戶支付打款超過五千萬,長期穩定運行,經過了線上長時間的驗證。

業務場景

微信的入賬通知:

技術點

1. 集成http依賴組件,調用微信官方支付接口

 <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.6</version>
</dependency>

2. 集成xml依賴組件,處理xml內容

<dependency>
    <groupId>jdom</groupId>
    <artifactId>jdom</artifactId>
    <version>1.1</version>
</dependency>

代碼演示

1. 項目目錄結構

2. pom.xml依賴組件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>com.md</groupId>
		<artifactId>spring-boot2-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<relativePath>../pom.xml</relativePath>
	</parent>

	<artifactId>spring-boot2-wechat-pay</artifactId>
	<packaging>jar</packaging>

	<name>spring-boot2-wechat-pay</name>
	<description>Spring Boot, MVC, Rest API for App</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<!-- 構建成可運行的Web項目 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib-ext-spring</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpmime</artifactId>
		</dependency>
		<dependency>
			<groupId>jdom</groupId>
			<artifactId>jdom</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
		</dependency>
		<dependency>
			<groupId>com.github.xiaoymin</groupId>
			<artifactId>swagger-bootstrap-ui</artifactId>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3. 支付服務工具類

PayServiceUtils:

package com.md.demo.pay.utils.variation;

import java.net.InetAddress;
import java.util.Collections;
import java.util.Map;
import java.util.SortedMap;

import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.md.demo.pay.utils.ConfigUtil;
import com.md.demo.pay.utils.FsClientWithCertSSL;
import com.md.demo.pay.utils.MapUtils;
import com.md.demo.pay.utils.PayCommonUtil;
import com.md.demo.pay.utils.StrUtil;
import com.md.demo.pay.utils.XMLUtil;
import com.md.demo.pay.utils.vo.PayWithdrawHis;

import lombok.extern.slf4j.Slf4j;

/**
 * 支付服務工具類
 * 
 * @author Minbo
 *
 */
@Slf4j
public class PayServiceUtils {

	/**
	 * 打款
	 */
	public static int doEpay(String appId, String openId, String ip, String amount, String orderId, String re_user_name,
			PayWithdrawHis objInfo, String projectFlag, String appIdName) {
		if (Strings.isNullOrEmpty(orderId) || Strings.isNullOrEmpty(openId)) {
			log.info("企業支付開始,openid: " + openId + "  orderId: " + orderId);
			return 1;
		}

		int amountInt = Integer.valueOf(amount).intValue();
		if (amountInt < 100 || Strings.isNullOrEmpty(amount)) {
			log.warn("企業支付開始=> 金額不得少於100分(1元)");
			return 2;
		}

		try {
			if (Strings.isNullOrEmpty(ip)) {
				InetAddress addr = InetAddress.getLocalHost();
				ip = addr.getHostAddress().toString();
			}
			log.info("企業支付開始,用戶:" + openId + "  訂單號:" + orderId + " 金額:" + amount + ", ip=" + ip);

			// 設置支付參數
			SortedMap<String, Object> parameters = getSignParams(appId, orderId, openId, amountInt, ip, re_user_name,
					projectFlag, appIdName);
			parameters.put("sign", PayCommonUtil.createSignPublic(Charsets.UTF_8.toString(), parameters, projectFlag));// sign簽名
			String requestXML = PayCommonUtil.getRequestXml(parameters);// 生成xml格式字符串
			String responseStr = FsClientWithCertSSL.doPost(ConfigUtil.PROMOTION_URL, requestXML, projectFlag);

			// 解析結果
			Map<String, Object> resutlMap = XMLUtil.doXMLParse(responseStr);
			log.info("企業付款,微信返回結果:resutlMap=" + resutlMap.toString());

			// 校驗響應結果return_code
			if (resutlMap != null && WeixinConstant.FAIL.equalsIgnoreCase(resutlMap.get("return_code").toString())) {
				log.error("企業支付調用失敗");
				return 3;
			}
			if (StrUtil.null2Str(resutlMap.get("err_code")).equals("SENDNUM_LIMIT")) {
				return 66;
			}
			if (StrUtil.null2Str(resutlMap.get("err_code")).equals("NOTENOUGH")) {
				return 99;
			}
			if (StrUtil.null2Str(resutlMap.get("err_code")).equals("SYSTEMERROR")
					&& StrUtil.null2Str(resutlMap.get("err_code_des")).equals("系統繁忙,請稍後再試")) {
				return 77;
			}
			if (StrUtil.null2Str(resutlMap.get("err_code")).equals("SYSTEMERROR")) {
				return 88;
			}

			if (resutlMap != null && WeixinConstant.FAIL.equals(resutlMap.get("result_code"))) {
				log.error("企業付款失敗:orderId=" + orderId + ",失敗原因:return_msg=" + resutlMap.get("return_msg") + ","
						+ objInfo.toString());
				// 真實姓名不一致
				// 非實名用戶賬號不可發放
				// 用戶賬號被凍結,無法付款
				if (StrUtil.null2Str(resutlMap.get("err_code")).equals("NAME_MISMATCH")
						|| StrUtil.null2Str(resutlMap.get("err_code")).equals("V2_ACCOUNT_SIMPLE_BAN")
						|| StrUtil.null2Str(resutlMap.get("err_code")).equals("NO_AUTH")) {
					if (objInfo != null) {
						objInfo.setFailErrorMsg(
								resutlMap.get("err_code").toString() + ":" + resutlMap.get("err_code_des").toString());
						objInfo.setOrderStatusName("提現失敗,金幣已返還--" + resutlMap.get("err_code_des").toString());
						return 4;
					}

					// 參數錯誤
				} else if (StrUtil.null2Str(resutlMap.get("err_code")).equals("PARAM_ERROR")) {
					if (objInfo != null) {
						objInfo.setFailErrorMsg(
								resutlMap.get("err_code").toString() + ":" + resutlMap.get("err_code_des").toString());
						objInfo.setOrderStatusName("提現失敗,金幣已返還--" + "支付失敗,請重新申請");
						return 4;
					}
				}

				String return_msg = resutlMap.get("return_msg").toString();
				if (return_msg.equals("openid與商戶appid不匹配")) {
					log.error("提現失敗【準備重試】,openid與商戶appid不匹配。" + objInfo.toString());
					return 44;
				}
				return 55;
			}

			if (WeixinConstant.SUCCESS.equalsIgnoreCase(resutlMap.get("result_code").toString())) {
				Map<String, Object> map = buildClientJson(resutlMap);
				log.info("企業付款成功:" + map.toString());
				return 0;
			}

		} catch (Exception e) {
			log.error("企業付款異常:" + e.getMessage(), e);
			return 5;
		}
		return 1;
	}

	/**
	 * 查詢指定訂單id狀態
	 */
	public static int doEquery(String appId, String outOrderNo, String projectFlag) {
		if (Strings.isNullOrEmpty(outOrderNo)) {
			return 1;
		}
		try {
			// 組裝查詢參數
			SortedMap<String, Object> params = buildQueryParams(appId, outOrderNo, projectFlag);
			String requestXML = PayCommonUtil.getRequestXml(params);// 生成xml格式字符串
			// 帶上post請求支付查詢接口
			String responseStr = FsClientWithCertSSL.doPost(ConfigUtil.PROMOTION_QUERY_URL, requestXML, projectFlag);
			SortedMap<String, Object> responseMap = XMLUtil.doXMLParse(responseStr);// 解析響應xml格式字符串
			log.info("企業付款查詢=> " + responseMap.toString());

			// 校驗響應結果return_code
			if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("return_code").toString())) {
				log.error("企業支付調用失敗");
				return 2;
			}

			// 校驗業務結果result_code
			if (WeixinConstant.FAIL.equalsIgnoreCase(responseMap.get("result_code").toString())) {
				if ("NOT_FOUND".equalsIgnoreCase(responseMap.get("err_code").toString())) {
					log.info("指定單號數據不存在,放行打款...");
					return 4;
				}
				return 3;
			}

			if (StrUtil.null2Str(responseMap.get("err_code")).equals("SYSTEMERROR")
					&& StrUtil.null2Str(responseMap.get("status")).equals("PROCESSING")) {
				// 扣款消耗失敗,退回提現
				if (StrUtil.null2Str(responseMap.get("reason")).equals("consumefund failed.")
						&& StrUtil.null2Str(responseMap.get("return_msg")).equals("consumefund failed.")) {
					log.error("扣款消耗失敗,consumefund failed,退回提現");
					return 99;
				}
				return 77;
			}

			if (StrUtil.null2Str(responseMap.get("err_code")).equals("SYSTEMERROR")) {
				return 88;
			}

			if (WeixinConstant.SUCCESS.equalsIgnoreCase(responseMap.get("result_code").toString())) {
				log.error("已打款,不能重複打款");
				return 0;
			}

			// 組裝響應數據
			Map<String, Object> resultMap = PayServiceUtils.buildResponse(responseMap);
			log.info("企業付款查詢結果:" + resultMap.toString());
			return -1;

		} catch (Exception e) {
			log.error("付款查詢異常:" + e.getMessage(), e);
			return 5;
		}
	}

	/**
	 * 組裝響應數據
	 * 
	 * @param resutlMap 付款響應結果
	 * @return
	 */
	private static Map<String, Object> buildClientJson(Map<String, Object> resutlMap) {

		if (resutlMap == null || resutlMap.isEmpty()) {
			return Collections.emptyMap();
		}
		Map<String, Object> returnMap = ImmutableMap.<String, Object>builder()
				.put("trade_no", resutlMap.get("partner_trade_no")).put("payment_no", resutlMap.get("payment_no"))
				.put("payment_time", resutlMap.get("payment_time")).put("result_msg", resutlMap.get("result_code"))
				.build();
		return returnMap;
	}

	/**
	 * 組合sign簽名參數
	 * 
	 * @return
	 */
	private static SortedMap<String, Object> getSignParams(String appId, String tradeNo, String openId, int amount,
			String ip, String re_user_name, String projectFlag, String appDesc) {
//		String appId = WeixinPayConfig.APPID;
		String mchId = WeixinPayConfig.MCHID;
		Map<String, Object> oparams = ImmutableMap.<String, Object>builder().put("mch_appid", appId)
//				.put("desc", WeixinConstant.EPAY_DESC)
				.put("desc", appDesc)
				// 企業付款描述信息
				.put("mchid", mchId)
				// 商戶號
				.put("nonce_str", PayCommonUtil.CreateNoncestr())
				// 16隨機字符串(大小寫字母加數字)
				.put("device_info", PayCommonUtil.createConceStr(32).toUpperCase())// 設備號 暫時寫死
				.put("partner_trade_no", tradeNo)// 商戶訂單號
				.put("openid", openId)// 用戶openid 注意:微信的openid
				.put("check_name", "NO_CHECK")// 不校驗真實姓名
//				.put("check_name", "FORCE_CHECK")// 校驗真實姓名
//				.put("re_user_name", re_user_name)// 校驗真實姓名
				.put("amount", amount)// 金額
				.put("spbill_create_ip", ip)// ip地址
				.build();
		return MapUtils.sortMap(oparams);
	}

	/**
	 * 封裝查詢結果數據
	 * 
	 * @param responseMap 查詢結果
	 * @return
	 */
	private static Map<String, Object> buildResponse(SortedMap<String, Object> responseMap) {
		Map<String, Object> returnMap = ImmutableMap.<String, Object>builder()
				.put("trade_no", responseMap.get("partner_trade_no")).put("payment_no", responseMap.get("detail_id"))
				.put("payment_account", responseMap.get("payment_amount"))
				.put("transfer_time", responseMap.get("transfer_time"))
				.put("result_code", responseMap.get("result_code")).build();
		return returnMap;
	}

	/**
	 * 組裝查詢參數
	 * 
	 * @param outTradeNo
	 * @return
	 */
	private static SortedMap<String, Object> buildQueryParams(String appId, String outTradeNo, String projectFlag) {
		String mchId = WeixinPayConfig.MCHID;
		// 組裝查詢參數- 可以使用treemap
		Map<String, Object> queryParams = ImmutableMap.<String, Object>builder().put("appid", appId)// 商戶號的appid
				.put("mch_id", mchId)// 商戶號
				.put("nonce_str", PayCommonUtil.CreateNoncestr())// 16隨機字符串(大小寫字母加數字)
				.put("partner_trade_no", outTradeNo)// 商戶訂單號
				.build();
		// key ASCII 排序
		SortedMap<String, Object> sortMap = MapUtils.sortMap(queryParams);
		// MD5簽名
		String createSign = PayCommonUtil.createSignPublic(Charsets.UTF_8.toString(), sortMap, projectFlag);
		sortMap.put("sign", createSign);
		return sortMap;
	}
}

4. 填寫相關微信後臺支付Key信息

這裏目前是寫到了代碼中,因爲一般很少變化。當然,你也可以做成配置信息,或者使用配置中心,我們最新代碼已上線配置中心Nacos。

WeixinConstant

package com.md.demo.pay.utils.variation;

/**
 * 微信支付常量類
 */
public class WeixinConstant {

	public static String SUCCESS = "SUCCESS"; // 成功return_code

	public static String FAIL = "FAIL"; // 失敗return_code

//	public static String EPAY_DESC = "申請提現轉賬";// 企業付款描述
}

WeixinPayConfig:

package com.md.demo.pay.utils.variation;

/**
 * 微信公衆號支付配置信息
 */
public class WeixinPayConfig {

//	// APPID(可固化,也可以動態變,DB中可同時記錄AppID)
//	public final static String APPID = "xxxxxx";

	// 公衆平臺商戶ID
	public final static String MCHID = "xxxxxx";

	// 公衆平臺商戶KEY
	public final static String KEY = "xxxxxx";

	// 微信企業支付證書
	public static String CERT_FILE = System.getProperty("user.dir") + System.getProperty("file.separator") + "cert"
			+ System.getProperty("file.separator") + "apiclient_cert.p12";
}

此項目中,只需要更改這幾個文件的信息就可以了。

最後把證書放到項目下的cert/目錄下即可,例如:

5. 提現記錄實體類

PayWithdrawHis

package com.md.demo.pay.utils.vo;

import java.io.Serializable;
import java.util.Date;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * <p>
 * 提現記錄
 * </p>
 *
 * @author minbo
 */
@Data
@ApiModel(value = "PayWithdrawHis對象", description = "提現記錄")
public class PayWithdrawHis implements Serializable {

	private static final long serialVersionUID = 1L;

	@ApiModelProperty(value = "訂單流水號")
	private String orderId;

	@ApiModelProperty(value = "系統用戶ID")
	private String sysUserId;

	@ApiModelProperty(value = "AppID")
	private String appId;

	@ApiModelProperty(value = "兌換金幣值")
	private String gold;

	@ApiModelProperty(value = "提現金額")
	private String income;

	@ApiModelProperty(value = "提現申請時間")
	private Date applyTime;

	@ApiModelProperty(value = "訂單狀態類型值,1/2/3/4")
	private Integer orderStatus;

	@ApiModelProperty(value = "訂單狀態類型名1處理中,2提現成功,3審覈中,4提現失敗")
	private String orderStatusName;

	@ApiModelProperty(value = "微信openID")
	private String wechatOpenId;

	@ApiModelProperty(value = "提現完成時間")
	private Date finishedTime;

	@ApiModelProperty(value = "提現失敗原因")
	private String failErrorMsg;

}

6. 支付服務接口

IWechatPayService:

package com.md.demo.service;

/**
 * <p>
 * 微信支付打款服務
 * </p>
 *
 * @author minbo
 */
public interface IWechatPayService {

	/**
	 * 微信批量打款
	 */
	public void wechatBatchPay();
}

WechatPayServiceImpl:

package com.md.demo.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.md.demo.pay.utils.StrUtil;
import com.md.demo.pay.utils.variation.PayServiceUtils;
import com.md.demo.pay.utils.vo.PayWithdrawHis;
import com.md.demo.service.IWechatPayService;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 服務實現類
 * </p>
 *
 * @author minbo
 */
@Service
@Slf4j
public class WechatPayServiceImpl implements IWechatPayService {

	/**
	 * 悅頭條-新版-微信批量打款
	 */
	@Override
	public void wechatBatchPay() {

		// TODO 這裏可從DB中獲得訂單數據
		List<PayWithdrawHis> list = new ArrayList<PayWithdrawHis>();
		loopFlag: for (PayWithdrawHis objInfo : list) {
			log.info("---------------------------------------------");
			log.info("--------------------start--------------------");
			try {

				// 這裏的openId,是當前應用或公衆號下的openId
				String openId = StrUtil.null2Str(objInfo.getWechatOpenId());

				String ip = "0.0.0.0";
				// 提現金幣,微信官方後臺是分爲單位,則要乘以100
				String amount = String.valueOf(Integer.valueOf(objInfo.getIncome()) * 100);
				// 訂單ID
				String orderId = objInfo.getOrderId();

//				// 獲得appId的配置,可以把支付信息等做成DB可配置項
//				PayAppConfig sdkPayAppConfig = this.sdkPayAppConfigService.getById(objInfo.getAppId());
//				String appIdName = sdkPayAppConfig.getProductName() + "的紅包";
//				String wxAppId = sdkPayAppConfig.getWxAppId();

				// 企業付款描述
				String appIdName = "填入賬信息詳情展示"; // 例如:XXX產品的紅包
				String wxAppId = "微信後臺的appId";

				// 1. 先查詢訂單的狀態
				int flag = PayServiceUtils.doEquery(wxAppId, orderId, null);
				if (flag == 99) {
					log.warn("查詢,扣款消耗失敗,退回打款");

					// TODO 扣款消耗失敗處理
					continue;

				}

				if (flag == 88) {
					log.error("查詢,後臺企業微信賬戶系統異常,停止打款", new RuntimeException("微信賬戶異常,停止此次打款"));
					break loopFlag;

				}

				if (flag == 77) {
					log.error("查詢,後臺企業微信賬戶系統訂單正在處理中", new RuntimeException("微信賬戶訂單正在處理中,跳過處理"));
					continue;

				}

				if (flag == 0) {
					log.info("之前已經打款成功,直接更新狀態即可。");
					// TODO 已打款成功,不重複打款,直接更新狀態處理
					continue;
				}

				// 不存在此訂單數據,真正開始打款
				if (flag == 4) {
					log.info("開始真正打款,然後更新訂單狀態...");
					int result = PayServiceUtils.doEpay(wxAppId, openId, ip, amount, orderId, null, objInfo, null,
							appIdName);

					if (result == 99) {
						log.error("打款,後臺企業微信賬戶支付餘額不足,停止打款", new RuntimeException("餘額不足,停止此次打款"));
						break loopFlag;
					}

					if (result == 88) {
						log.error("打款,後臺企業微信賬戶系統異常,停止打款。" + objInfo.toString(), new RuntimeException("微信賬戶異常,停止此次打款"));
						break loopFlag;
					}

					if (result == 77) {
						log.error("打款速度過快,微信賬戶處理繁忙,休眠十秒。" + objInfo.toString());
						Thread.sleep(10000);
						continue;
					}

					if (result == 66) {
						log.error("該用戶今日付款次數超過限制,如有需要請登錄微信支付商戶平臺更改API安全配置。",
								new RuntimeException("該用戶今日付款次數超過限制,跳過該用戶的打款"));
						continue;
					}

					if (result == 55) {
						log.error("打款報錯【微信】,請檢查。" + objInfo.toString(), new RuntimeException("打款報錯,跳過該用戶打款"));
						continue;
					}

					// 最後結果處理
					if (result == 0) {
						// TODO 打款成功處理

					} else if (result == 4) {
						// TODO 打款失敗處理
					}
				}
			} catch (Exception e) {
				log.error("提現記錄異常:orderId=" + objInfo.getOrderId() + ", msg=" + e.getMessage(), e);
			}
			log.info("--------------------end--------------------");
			log.info("---------------------------------------------");
		}
	}


}

7. 計劃任務

ScheduledTasks:

package com.md.demo.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.md.demo.service.IWechatPayService;

@Component
public class ScheduledTasks {

	protected static Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);

	@Autowired
	private IWechatPayService wechatEpayService;

	/**
	 * 每60秒執行一次,從DB中獲得訂單數據
	 */
	@Scheduled(initialDelay = 5000, fixedDelay = 60000)
	public void httTaskOfWechatPay() {
		logger.info("------------------------------------------------");
		logger.info("============微信打款任務,start===================");

		// TODO 調用打款服務
//		this.wechatEpayService.wechatBatchPayHtt();

		logger.info("============微信打款任務,end===================");
		logger.info("------------------------------------------------");
	}
}

8. 啓動類

Application:

package com.md.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * 程序主入口
 * 
 * @author Minbo
 *
 */
@SpringBootApplication
@EnableScheduling
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	/**
	 * 開啓過濾器功能
	 * 
	 * @return
	 */
	private CorsConfiguration buildConfig() {
		CorsConfiguration corsConfiguration = new CorsConfiguration();
		corsConfiguration.addAllowedOrigin("*");
		corsConfiguration.addAllowedHeader("*");
		corsConfiguration.addAllowedMethod("*");
		return corsConfiguration;
	}

	/**
	 * 跨域過濾器
	 * 
	 * @return
	 */
	@Bean
	public CorsFilter corsFilter() {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", buildConfig());
		return new CorsFilter(source);
	}
}

本地測試

1. WechatPayServiceTest

package com.md.demo.pay.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.md.demo.pay.utils.variation.PayServiceUtils;

/**
 * 本地測試類
 * 
 * @author Minbo
 *
 */
@Service
public class WechatPayServiceTest {

	protected static Logger logger = LoggerFactory.getLogger(WechatPayServiceTest.class);

	// 測試
	public static void main(String[] args) {

		// 訂單查詢
//		wechat.doEquery("996846M20180330115059R5X9H9MHM", null);

		// 支付打款
		PayServiceUtils.doEpay("wx3ff92cec62c097d1", "o7Hzn0rr6U3Xb7Q1W9M-bH7SQXys", "192.168.1.1", "100",
				"996846M20180330115059R5X9H9MHY", null, null, null, "xxx的紅包");

		System.out.println("done");
	}

}

 

如果提示例如“AppID和商戶號不匹配”,“微信openId與AppID不匹配”,“本地IP不允許調用支付接口”,“餘額不足”等提示,則證明已經集成成功。像“本地IP不允許調用支付接口”,只需要把本地電腦的公網IP配置到微信後臺即可。

完整源碼下載

我的Github源碼地址:

https://github.com/hemin1003/spring-boot-study/tree/master/spring-boot2-study/spring-boot2-parent/spring-boot2-wechat-pay

該系列教程

SpringBoot從入門到精通教程

我的專欄

 

 

至此,全部介紹就結束了

 

 

-------------------------------

-------------------------------

 

我的CSDN主頁

關於我(個人域名)

我的開源項目集Github

 

期望和大家一起學習,一起成長,共勉,O(∩_∩)O謝謝

歡迎交流問題,可加個人QQ 469580884,

或者,加我的羣號 751925591,一起探討交流問題

不講虛的,只做實幹家

Talk is cheap,show me the code

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章