SpringBoot從入門到精通教程(三十)- 支付寶企業支付集成(五分鐘集成)

需求背景

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

問題痛點

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

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

業務場景

支付寶的入賬通知:

技術點

1. 集成支付寶官方支付SDK

<dependency>
    <groupId>alipay-sdk</groupId>
    <artifactId>alipay-sdk</artifactId>
    <version>1.0</version>
</dependency>

說明:需要進入項目根目錄下的lib目錄,運行下面命令,把包安裝到maven本地倉庫

mvn install:install-file -Dfile=alipay-sdk-1.0.jar -DgroupId=alipay-sdk -DartifactId=alipay-sdk -Dversion=1.0 -Dpackaging=jar

代碼演示

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-alipay</artifactId>
	<packaging>jar</packaging>

	<name>spring-boot2-alipay</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.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>
		<dependency>
			<groupId>alipay-sdk</groupId>
			<artifactId>alipay-sdk</artifactId>
			<version>1.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3. 支付服務工具類

AliPayServiceUtils:

package com.md.demo.pay.util;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayFundTransOrderQueryRequest;
import com.alipay.api.request.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.response.AlipayFundTransOrderQueryResponse;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.md.demo.pay.utils.vo.PayWithdrawHis;
import com.md.demo.util.StrUtil;

import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;

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

	// TODO 填寫支付寶接入信息
	private static final String APP_ID = "xxxxxx";
	private static final String APP_PRIVATE_KEY = "xxxxxx";
	private static final String ALIPAY_PUBLIC_KEY = "xxxxxx";

	private static AlipayClient alipayClient;

	private static final String payer_show_name = "xxx企業賬戶";
	private static final String remark = "提現轉賬測試";

	static {
		alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json",
				"UTF-8", ALIPAY_PUBLIC_KEY, "RSA2");
	}

	/**
	 * 轉賬打款
	 */
	public static int transfer(String out_biz_no, String payee_account, String amount, String payee_real_name,
			PayWithdrawHis objInfo) {
		AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
		request.setBizContent(
				"{" + "    \"out_biz_no\":\"" + out_biz_no + "\"," + "    \"payee_type\":\"ALIPAY_LOGONID\","
						+ "    \"payee_account\":\"" + payee_account + "\"," + "    \"amount\":\"" + amount + "\","
						+ "    \"payer_show_name\":\"" + payer_show_name + "\"," + "    \"payee_real_name\":\""
						+ payee_real_name + "\"," + "    \"remark\":\"" + remark + "\"," + "  }");
		try {
			AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
			String json = response.getBody();
			log.info("打款操作結果json=" + json);

			JSONObject obj = JSONObject.fromObject(json);
			JSONObject objResp = JSONObject.fromObject(obj.get("alipay_fund_trans_toaccount_transfer_response"));
			if (StrUtil.null2Str(objResp.get("code")).equals("10000")) {
				log.info("打款成功");
				return 0;

			} else {
				if (StrUtil.null2Str(objResp.get("sub_code")).equals("PAYER_BALANCE_NOT_ENOUGH")) {
					return 99;
				}
				if (StrUtil.null2Str(objResp.get("sub_code")).equals("aop.SYSTEM_ERROR")) {
					return 88;
				}
				if (StrUtil.null2Str(objResp.get("sub_code")).equals("isp.unknow-error")
						&& StrUtil.null2Str(objResp.get("sub_msg")).equals("系統繁忙")) {
					return 77;
				}

				if (objInfo != null) {
					// 支付寶賬號和姓名不匹配,請確認姓名是否正確
					// 收款賬號不存在
					if (StrUtil.null2Str(objResp.get("sub_code")).equals("PAYEE_USER_INFO_ERROR")
							|| StrUtil.null2Str(objResp.get("sub_code")).equals("PAYEE_NOT_EXIST")) {
						objInfo.setFailErrorMsg(StrUtil.null2Str(objResp.get("sub_code")) + ":"
								+ StrUtil.null2Str(objResp.get("sub_msg")));
						objInfo.setOrderStatusName("提現失敗,金幣已返還-" + StrUtil.null2Str(objResp.get("sub_msg")));

						log.error("打款失敗。objResp=" + objResp.toString() + ",httWithdrawHisVo--->" + objInfo.toString());
						return 4;
					}
				}
				return 55;
			}

		} catch (AlipayApiException e) {
			log.error("轉賬異常:" + e.getMessage(), e);
			return 2;
		}
	}

	/**
	 * 訂單查詢
	 */
	public static int query(String out_biz_no) {
		AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest();
		request.setBizContent("{" + "    \"out_biz_no\":\"" + out_biz_no + "\" " + "}");

		try {
			AlipayFundTransOrderQueryResponse response = alipayClient.execute(request);
			String json = response.getBody();
			log.info("查詢打款結果json=" + json);

			JSONObject obj = JSONObject.fromObject(json);
			JSONObject objResp = JSONObject.fromObject(obj.get("alipay_fund_trans_order_query_response"));

			if (StrUtil.null2Str(objResp.get("code")).equals("10000")
					&& StrUtil.null2Str(objResp.get("msg")).equals("Success")) {
				log.error("訂單已打款,不能重複打款。out_biz_no=" + out_biz_no + ",json=" + json);
				return 0;
			}
			if (StrUtil.null2Str(objResp.get("code")).equals("40004")
					&& StrUtil.null2Str(objResp.get("sub_code")).equals("ORDER_NOT_EXIST")) {
				log.info("訂單不存在,可以打款");
				return 4;

			} else {
				return 1;
			}
		} catch (AlipayApiException e) {
			log.error("查詢異常:" + e.getMessage(), e);
			return 3;
		}
	}
}

4. 提現記錄實體類

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 = "打款賬戶號")
	private String Account;
	
	@ApiModelProperty(value = "賬戶姓名")
	private String name;

	@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 = "提現完成時間")
	private Date finishedTime;

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

}

5. 支付服務接口

IAlipayService:

package com.md.demo.pay.service;

/**
 * 支付寶自動打款
 * 
 * @author Minbo
 */
public interface IAlipayService {

	/**
	 * 支付寶批量打款
	 */
	public void alipayBatchPay();

}

AlipayServiceImpl:

package com.md.demo.pay.service.impl;

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

import org.springframework.stereotype.Service;

import com.md.demo.pay.service.IAlipayService;
import com.md.demo.pay.util.AliPayServiceUtils;
import com.md.demo.pay.utils.vo.PayWithdrawHis;
import com.md.demo.util.StrUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 支付寶自動打款
 * 
 * @author Minbo
 */
@Service
@Slf4j
public class AlipayServiceImpl implements IAlipayService {

	/**
	 * 支付寶批量打款
	 */
	@Override
	public void alipayBatchPay() {

		// TODO 這裏從DB中獲得訂單數據
		List<PayWithdrawHis> list = new ArrayList<PayWithdrawHis>();

		for (PayWithdrawHis objInfo : list) {
			log.info("---------------------------------------------");
			log.info("--------------------start--------------------");

			try {
				String name = StrUtil.null2Str(objInfo.getName());
				String amount = objInfo.getIncome();
				String orderId = objInfo.getOrderId();
				String account = StrUtil.null2Str(objInfo.getAccount());

				log.info("打款訂單:" + objInfo.toString());

				// 1. 先查詢此訂單狀態
				int flag = AliPayServiceUtils.query(orderId);
				if (flag == 0) {
					log.info("之前已經打款成功,直接更新狀態即可。");

					// TODO 直接更新訂單樁體
//					this.updateHttWithdrawHis(objInfo);

					// 2. 不存在此訂單數據,則打款
				} else if (flag == 4) {
					log.info("開始真正打款,然後更新狀態。");
					int result = AliPayServiceUtils.transfer(orderId, account, amount, name, objInfo);

					if (result == 99) {
						log.error("打款,後臺企業支付寶賬戶支付餘額不足,停止打款", new RuntimeException("餘額不足,停止此次打款"));
						break;
					}
					if (result == 88) {
						log.error("打款,後臺企業支付寶賬戶系統異常,停止打款。" + objInfo.toString(),
								new RuntimeException("支付寶賬戶異常,停止此次打款"));
						break;
					}
					if (result == 77) {
						log.error("支付寶賬戶系統繁忙,休眠十秒。" + objInfo.toString());
						Thread.sleep(10000);
					}
					if (result == 55) {
						log.error("打款報錯【支付寶】,請檢查。" + objInfo.toString(), new RuntimeException("打款報錯,跳過該用戶打款"));
						continue;
					}

					if (result == 0) {
						// TODO 打款成功處理
//						this.updateHttWithdrawHis(objInfo);

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

//	/**
//	 * 更新提現記錄-‘提現成功’狀態
//	 * 
//	 * @param httWithdrawHisVo
//	 */
//	private void updateHttWithdrawHis(PayWithdrawHis httWithdrawHisVo) {
//		httWithdrawHisVo.setStatus("提現成功");
//		httWithdrawHisVo.setStatusType(2);
//		httWithdrawHisVo.setUpdateDate(DateUtil.getCurrentLongDateTime());
//		boolean status = this.httWithdrawHisService.updateHttWithdrawHis(httWithdrawHisVo);
//		if (status) {
//			log.info("狀態更新成功 for 提現成功");
//		} else {
//			log.info("狀態更新失敗 for 提現成功");
//		}
//	}
}

注:改動TODO標記的地方,添加自己的業務邏輯即可

6. 計劃任務

ScheduledTasks:

package com.md.demo.pay.task;

import java.util.Calendar;
import java.util.Date;

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.pay.service.IAlipayService;

@Component
public class ScheduledTasks {

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

	@Autowired
	private IAlipayService alipayService;

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

		// 調用打款服務
//		this.alipayService.alipayBatchPay();

		logger.info("============支付寶打款任務,end===================");
		logger.info("------------------------------------------------");
	}

	/**
	 * 是否允許自動打款-判斷時間區間
	 * 
	 * @return
	 */
	public boolean isGoPayment() {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		logger.info("當前小時hour=" + hour);

		if (hour >= 0 && hour <= 8) {
			logger.info("0點到8點期間,不進行打款,停止打款任");
			return false;
		}
		return true;
	}

}

7. 啓動類

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);
	}
}

完整源碼下載

我的Github源碼地址:

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

該系列教程

SpringBoot從入門到精通教程

我的專欄

 

 

至此,全部介紹就結束了

 

 

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

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

 

我的CSDN主頁

關於我(個人域名)

我的開源項目集Github

 

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

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

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

不講虛的,只做實幹家

Talk is cheap,show me the code

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