1.1 業務背景
1.2 為什麼要使用支付寶支付?當今網際網路,湧現了許許多多的電子商務平臺,比如淘寶網、京東、還有一些不知名的電商平臺,但是並不是所有的公司都有與銀行合作開發金融業務的資質,但是像一些網際網路大廠就有這樣的資質,比如螞蟻金服的支付寶、騰訊的財付通以及京東金融,其他沒有資質的公司那麼要實現支付業務那麼必須呼叫其他平臺的一個交易介面。
1.3 如何使用支付寶支付?(以沙箱環境為例)支付寶是螞蟻金服旗下的一個產品,它穩定、安全並且可靠,並且介面、開發文件提供地非常全面。
1.準備步驟
2. 然後下載一個支付寶開放平臺開發助手。
【連結下載】
3. 生成支付寶介面驗籤所需金鑰
4. 設定沙箱環境中的應用公鑰
5. 設定支付寶非同步回撥地址
至此準備工作完畢
1.3.1 首先構建一個maven工程,然後引入alipay-sdk依賴包。
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.9.71.ALL</version>
</dependency>
1.3.2 寫一個alipay.propeties用於儲存專案中需要使用到的支付寶支付配置資訊。
內容
//這是支付寶沙箱環境閘道器 如果是生產環境換成正式地址即可
alipay_url=https://openapi.alipaydev.com/gateway.do
app_id=沙箱環境的appid
app_private_key=應用私鑰
alipay_public_key=支付寶公鑰
# 同步回撥地址 重定向地址本地瀏覽器
return_payment_url=同步回撥地址
return_order_url=同步回撥地址跳轉目標地址
# 非同步通知地址 公網介面(webService)
notify_payment_url=非同步通知地址
沙箱環境appid
應用私鑰
* 支付寶公鑰
1.3.3 編寫一個支付寶支付配置類。
package com.qingyun.gmall.payment.config;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-03-14
* Time: 18:15
* Explain: 支付寶支付配置類
*/
@Configuration
@PropertySource("classpath:alipay.properties")
public class AlipayConfig {
@Value("${alipay_url}")
private String alipay_url;
@Value("${app_private_key}")
private String app_private_key;
@Value("${app_id}")
private String app_id;
public final static String format="json";
public final static String charset="utf-8";
public final static String sign_type="RSA2";
public static String return_payment_url;
public static String notify_payment_url;
public static String return_order_url;
public static String alipay_public_key;
@Value("${alipay_public_key}")
public void setAlipay_public_key(String alipay_public_key) {
AlipayConfig.alipay_public_key = alipay_public_key;
}
@Value("${return_payment_url}")
public void setReturn_url(String return_payment_url) {
AlipayConfig.return_payment_url = return_payment_url;
}
@Value("${notify_payment_url}")
public void setNotify_url(String notify_payment_url) {
AlipayConfig.notify_payment_url = notify_payment_url;
}
@Value("${return_order_url}")
public void setReturn_order_url(String return_order_url) {
AlipayConfig.return_order_url = return_order_url;
}
@Bean
public AlipayClient alipayClient(){
AlipayClient alipayClient=new DefaultAlipayClient(alipay_url,app_id,app_private_key,format,charset, alipay_public_key,sign_type );
return alipayClient;
}
}
1.3.4 請求處理控制器實現
@Controller
@LoginRequired(mustLogin = true)
@Slf4j
public class PaymentController {
@Reference
private OrderService orderService;
@Reference
private PaymentService paymentService;
/**
*支付方式選擇頁面
*/
@RequestMapping(value = "/index")
public String index(String orderSN, HttpServletRequest request, ModelMap modelMap){
//獲取當前登入使用者
UmsMember umsMember =(UmsMember) request.getAttribute(CommonConstant.REQ_ATTR_USERINFO);
if (umsMember == null) {
throw new RuntimeException(ResultEnum.MEMBER_NOT_LOGIN.getMsg());
}
//通過orderSN與使用者編號獲取訂單資訊
OmsOrder omsOrder=orderService.getOrderByOrderSN(umsMember.getId(),orderSN);
modelMap.addAttribute("orderId",omsOrder.getOrderSn());
modelMap.addAttribute("totalAmount",omsOrder.getTotalAmount());
return "index";
}
/**
*發起支付寶支付
*/
@RequestMapping(value = "/alipay/submit")
@ResponseBody
public String alipaySubmit(String orderSN,HttpServletRequest request){
//獲取當前登入使用者
UmsMember umsMember =(UmsMember) request.getAttribute(CommonConstant.REQ_ATTR_USERINFO);
if (umsMember == null) {
throw new RuntimeException(ResultEnum.MEMBER_NOT_LOGIN.getMsg());
}
String resultHTML=paymentService.luanchAlipay(orderSN,umsMember);
return resultHTML;
}
/**
*同步回撥
*/
@RequestMapping(value = "/alipay/callback/return")
public String alipayReturn(@RequestParam HashMap<String,String> notifyReqParam){
if (StringUtils.isBlank(notifyReqParam.get("sign"))){
return "failed";
}
return "finish";
}
/**
*支付寶非同步通知請求處理器
*/
@PostMapping(value = "/alipay/callback/notify")
@ResponseBody
public String alipayNotify(@RequestParam HashMap<String,String> notifyReqParam){
return paymentService.checkAlipay(notifyReqParam);
}
}
1.3.5 支付寶支付,業務邏輯層實現。
package com.qingyun.gmall.payment.service.impl;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.qingyun.gmall.bean.OmsOrder;
import com.qingyun.gmall.bean.PaymentInfo;
import com.qingyun.gmall.bean.UmsMember;
import com.qingyun.gmall.constants.CommonConstant;
import com.qingyun.gmall.enums.PayTypeEnum;
import com.qingyun.gmall.enums.ResultEnum;
import com.qingyun.gmall.payment.config.AlipayConfig;
import com.qingyun.gmall.payment.enums.PaymentStatusEnum;
import com.qingyun.gmall.payment.mapper.PaymentInfoMapper;
import com.qingyun.gmall.payment.params.AlipayReqParam;
import com.qingyun.gmall.service.OrderService;
import com.qingyun.gmall.service.PaymentService;
import com.qingyun.gmall.utils.ActiveMQUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import javax.jms.JMSException;
import java.util.Date;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-03-14
* Time: 18:36
* Explain: 支付業務實現類
*/
@Service
@Slf4j
public class PaymentServiceImpl implements PaymentService {
@Autowired
private AlipayClient alipayClient;
@Autowired
private PaymentInfoMapper paymentInfoMapper;
@Reference
private OrderService orderService;
@Autowired
private ActiveMQUtil activeMQUtil;
/**
* 發起支付寶支付
*
* @param orderSN
* @param umsMember
* @return 支付寶介面返回html頁面資訊
*/
@Override
public String luanchAlipay(String orderSN, UmsMember umsMember) {
//根據訂單號與會員id獲取當前訂單資訊
OmsOrder omsOrder = orderService.getOrderByOrderSN(umsMember.getId(), orderSN);
if (omsOrder==null){
throw new RuntimeException("不存在當前訂單資訊!orderSN="+orderSN);
}
String resultHTML=null;
AlipayReqParam alipayReqParam=new AlipayReqParam();
alipayReqParam.setOut_trade_no(orderSN);
alipayReqParam.setSubject(omsOrder.getSubject());
alipayReqParam.setTotal_amount(omsOrder.getTotalAmount());
//利用支付寶客戶端生成表單頁面
AlipayTradePagePayRequest alipayTradePayRequest=new AlipayTradePagePayRequest();
alipayTradePayRequest.setReturnUrl(AlipayConfig.return_order_url);
alipayTradePayRequest.setNotifyUrl(AlipayConfig.notify_payment_url);
System.out.println(JSON.toJSONString(alipayReqParam));
alipayTradePayRequest.setBizContent(JSON.toJSONString(alipayReqParam));
try {
resultHTML=alipayClient.pageExecute(alipayTradePayRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
//查詢是否已經建立過當前支付資訊
Example example=new Example(PaymentInfo.class);
example.createCriteria().andEqualTo("orderSn",orderSN);
PaymentInfo info = paymentInfoMapper.selectOneByExample(example);
if (info==null){
PaymentInfo paymentInfo=new PaymentInfo();
paymentInfo.setOrderSn(orderSN);
paymentInfo.setCreateTime(new Date());
paymentInfo.setSubject(omsOrder.getSubject());
paymentInfo.setPaymentStatus(PaymentStatusEnum.NOT_PAY.getDecr());
paymentInfo.setTotalAmount(omsOrder.getTotalAmount());
paymentInfo.setOrderId(omsOrder.getId());
boolean flag = paymentInfoMapper.insertSelective(paymentInfo)>0;
if (!flag) {
throw new RuntimeException("儲存支付資訊失敗!");
}
}
//傳送一條訊息到延時佇列中 用於後期檢查訂單支付狀態
HashMap<String,Object> msgMap=new HashMap<>();
msgMap.put("orderSN",orderSN);
msgMap.put("sendCount",1);
try {
activeMQUtil.sendDelayMapMessage(CommonConstant.PAYMENT_STATUS_CHECK_QUEUE_NAME,msgMap,1,activeMQUtil.getConnectionFactory().createConnection());
} catch (JMSException e) {
log.error("【支付寶支付】傳送延時訊息失敗");
e.printStackTrace();
}
return resultHTML;
}
/**
* 檢查支付寶支付非同步通知資訊
*
* @param notifyReqParam
* @return 返回處理結果資訊
*/
@Override
@Transactional
public String checkAlipay(HashMap<String, String> notifyReqParam) {
log.info("------callbackstart 支付寶開始回撥"+notifyReqParam.toString());
boolean isCheckPass=false;
try {
isCheckPass= AlipaySignature.rsaCheckV1(notifyReqParam,AlipayConfig.alipay_public_key,AlipayConfig.charset,AlipayConfig.sign_type);
} catch (AlipayApiException e) {
e.printStackTrace();
}
if (!isCheckPass) {
log.warn("-------驗籤不通過");
return "failure";
}
log.warn("-------驗籤通過");
//驗籤成功標誌
String trade_status=notifyReqParam.get("trade_status");
if (trade_status.equals("TRADE_SUCCESS")){
//檢查當前支付狀態
String outTradeNo=notifyReqParam.get("out_trade_no");
PaymentInfo paymentInfoQueryParams=new PaymentInfo();
paymentInfoQueryParams.setOrderSn(outTradeNo);
PaymentInfo paymentInfo=paymentInfoMapper.selectOne(paymentInfoQueryParams);
if (paymentInfo==null){
return "error:not exists out_trade_no:"+paymentInfoQueryParams.getOrderSn();
}
log.info("檢查是否已處理="+paymentInfo.getOrderSn());
if (paymentInfo.getPaymentStatus().equals(PaymentStatusEnum.PAID.getDecr())){
log.info("---已處理="+paymentInfo.getOrderSn());
return "success";
}else{
//更新支付資訊狀態
log.info("---未處理,更新支付狀態="+paymentInfo.getOrderSn());
paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getDecr());
paymentInfo.setConfirmTime(new Date());
paymentInfo.setCallbackContent(notifyReqParam.toString());
paymentInfo.setAlipayTradeNo(notifyReqParam.get("trade_no"));
sendPaymentSuccessMsg(paymentInfo);
return "success";
}
}
return "";
}
/**
* 檢查支付寶支付狀態
*
* @param orderSN
* @return 是否支付成功
*/
@Override
public boolean checkAlipayStatus(String orderSN) {
boolean flag=false;
AlipayTradeQueryRequest tradeQueryRequest=new AlipayTradeQueryRequest();
AlipayReqParam alipayReqParam=new AlipayReqParam();
alipayReqParam.setOut_trade_no(orderSN);
tradeQueryRequest.setBizContent(JSON.toJSONString(alipayReqParam));
try {
AlipayTradeQueryResponse response = alipayClient.execute(tradeQueryRequest);
if (response.isSuccess()) {
String tradeStatus = response.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)){
log.info("訂單:{}支付成功完成!",orderSN);
flag=true;
//更新當前支付資訊狀態 這裡需要進行冪等性檢查 冪等性指的就是在多次請求中返回的結果都是一致的
//查詢資料庫中當前支付狀態
PaymentInfo queryParam=new PaymentInfo();
queryParam.setOrderSn(orderSN);
PaymentInfo paymentInfo=paymentInfoMapper.selectOne(queryParam);
if (paymentInfo!=null){
if (paymentInfo.getPaymentStatus().equals(PaymentStatusEnum.NOT_PAY.getDecr())) {
//如果當前訂單狀態為未支付則修改當前訂單狀態
paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getDecr());
paymentInfo.setConfirmTime(new Date());
paymentInfo.setCallbackContent(response.getBody());
paymentInfo.setAlipayTradeNo(response.getTradeNo());
boolean result=paymentInfoMapper.updateByPrimaryKeySelective(paymentInfo)>0;
if (result){
sendPaymentSuccessMsg(paymentInfo);
flag=true;
log.info("當前訂單支付狀態修改成功!");
}else{
log.error("當前訂單支付狀態修改失敗!");
}
}else{
flag=true;
log.info("當前訂單已被處理過了!");
}
}else{
log.info("當前訂單支付資訊不存在!orderSN={}",orderSN);
throw new RuntimeException("當前訂單支付資訊不存在!orderSN="+orderSN);
}
}else{
log.info("訂單:{}尚未完成!",orderSN);
}
}else{
log.info("訂單:{}尚未完成!",orderSN);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return flag;
}
private void sendPaymentSuccessMsg(PaymentInfo paymentInfo) {
paymentInfoMapper.updateByPrimaryKeySelective(paymentInfo);
//傳送非同步通知給訂單
HashMap<String, Object> msgMap = new HashMap<>();
msgMap.put("orderSN", paymentInfo.getOrderSn());
msgMap.put("result", String.valueOf(ResultEnum.SUCCESS.getCode()));
msgMap.put("payType", String.valueOf(PayTypeEnum.ALI_PAY.getCode()));
try {
activeMQUtil.sendMapMessage(CommonConstant.PAYMENT_SUCCESS_QUEUE_NAME, msgMap, activeMQUtil.getConnectionFactory().createConnection());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
注意上面使用到了一個支付寶請求引數類AlipayReqParam:
package com.qingyun.gmall.payment.params;
import lombok.Data;
import java.math.BigDecimal;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-03-14
* Time: 18:20
* Explain: 支付寶支付請求引數
*/
@Data
public class AlipayReqParam {
private String out_trade_no;
private String product_code="FAST_INSTANT_TRADE_PAY";
private BigDecimal total_amount=new BigDecimal("0.01");
private String subject;
private String body;
private String passback_params;
private HashMap<String,String> extend_params;
}