一、前期准备:
SpringBoot对接支付宝当面付和手机网站支付_springboot 支付宝当面付_Biubiubiuexo的博客
配置成功后获得到我们开发需要的:支付宝公钥、商户私钥、应用ID
二、代码实现:
1.新建Merchant商户实体类:
package com.yuheng.payment.models;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* 收款商户
* @author michael
* @since 2022/11/15
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
public class Merchant {
/**
* 应用appID
*/
private String appId;
/**
* 应用密钥
*/
private String alipayAppKey;
/**
* 商户密钥
*/
private String mchKey;
/**
* 第三方应用商户token(若为空则为普通支付模式,不为空为服务商代调用模式)
*/
private String appAuthToken;
}
2.支付宝服务层:
package com.yuheng.payment.services;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import com.yuheng.payment.constants.PayStatus;
import com.yuheng.payment.exceptions.InternalRuntimeException;
import com.yuheng.payment.exceptions.PrePayResponseException;
import com.yuheng.payment.models.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Map;
import static java.lang.System.out;
/**
* 支付宝官方
* @author michael
* @since 2022/11/15
*/
public class ZhifubaoService implements SuperService {
private static final Logger log = LoggerFactory.getLogger(ZhifubaoService.class);
private static String serviceUrl = "https://openapi.alipay.com/gateway.do";
/**
* 支付宝扫码支付
* @param request
* @param merchant
* @return
* @throws AlipayApiException
*/
@Override
public PrePayResponse zfbQr(PrePayRequest request, Merchant merchant) throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
merchant.getAppId(),merchant.getMchKey(),"json","UTF-8",merchant.getAppKey(),"RSA2");
AlipayTradePrecreateRequest alipayTradePrecreateRequest = new AlipayTradePrecreateRequest();
alipayTradePrecreateRequest.setReturnUrl(request.getReturnUrl());
alipayTradePrecreateRequest.setNotifyUrl(request.getNotifyUrl());
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", request.getOrderNo());
// 传入金额单位为 分,换成支付宝单位 元
bizContent.put("total_amount", Double.toString(request.getAmount() / 100.00));
bizContent.put("subject", request.getBody());
alipayTradePrecreateRequest.setBizContent(bizContent.toString());
String resultStr = null;
// 为第三方应用代调用模式
if (StrUtil.isNotEmpty(merchant.getAppAuthToken())){
alipayTradePrecreateRequest.putOtherTextParam("app_auth_token", merchant.getAppAuthToken());
resultStr = alipayClient.execute(alipayTradePrecreateRequest,null,merchant.getAppAuthToken()).getBody();
}else {
// 普通商家模式
resultStr = alipayClient.execute(alipayTradePrecreateRequest).getBody();
}
JSONObject jsonObject = JSONObject.parseObject(resultStr).getJSONObject("alipay_trade_precreate_response");
String code = jsonObject.getString("code");
if (!"10000".equals(code)){
log.error("支付发起失败{}", jsonObject.getString("sub_msg"));
}
String qrCode = jsonObject.getString("qr_code");
String outTradeNo = jsonObject.getString("out_trade_no");
// return jsonObject.getJSONObject("alipay_trade_precreate_response").getString("qr_code");
return PrePayResponse
.builder()
.orderNo(outTradeNo)
.isRedirect(false)
.payText(qrCode)
.payStatus(PayStatus.PAYING)
.build();
}
/**
* 手机网页支付
* @param
* @param merchant
* @param request
* @return
* @throws AlipayApiException
*/
@Override
public PrePayResponse zfbWeb(PrePayRequest request, Merchant merchant) throws AlipayApiException, IOException {
AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
merchant.getAppId(),merchant.getMchKey(),"json","UTF-8",merchant.getAppKey(),"RSA2");
AlipayTradeWapPayRequest alipayTradeWapPayRequest = new AlipayTradeWapPayRequest();
alipayTradeWapPayRequest.setNotifyUrl(request.getNotifyUrl());
alipayTradeWapPayRequest.setReturnUrl(request.getReturnUrl());
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", request.getOrderNo());
// 传入金额单位为 分,换成支付宝单位 元
bizContent.put("total_amount", Double.toString(request.getAmount() / 100.00));
bizContent.put("subject", request.getBody());
bizContent.put("product_code", "QUICK_WAP_WAY");
alipayTradeWapPayRequest.setBizContent(bizContent.toString());
// 为第三方应用代调用模式
if (StrUtil.isNotEmpty(merchant.getAppAuthToken())){
alipayTradeWapPayRequest.putOtherTextParam("app_auth_token", merchant.getAppAuthToken());
}
AlipayTradeWapPayResponse alipayTradeWapPayResponse = alipayClient.pageExecute(alipayTradeWapPayRequest,"get");
String path = alipayTradeWapPayResponse.getBody();
return PrePayResponse
.builder()
.payText(path)
.isRedirect(false)
.payStatus(PayStatus.PAYING)
.build();
}
/**
* @param request
* 支付宝异步通知
* @return
*/
@Override
public PayNotify payNotify(HttpServletRequest request) {
// 获取支付宝POST过来反馈信息
Map<String, String> params = AliPayApi.toMap(request);
log.debug("收到支付宝支付原始通知:"+params);
PayNotify notifyResponse = PayNotify.builder()
.orderNo(params.get("out_trade_no"))
.outOrderNo(params.get("trade_no"))
.payStatus(PayStatus.PAYING)
.build();
if ("TRADE_SUCCESS".equals( params.get("trade_status"))) {
notifyResponse.setPayTime(timeFormat(params.get("gmt_payment")));
notifyResponse.setPayStatus(PayStatus.PAID);
}
notifyResponse.setRawSignObject(params);
return notifyResponse;
}
/**
* 签名验证
* @param response
* @param merchant
* @return
* @throws AlipayApiException
*/
@Override
public boolean verifyPayNotify(PayNotify response, Merchant merchant) throws AlipayApiException {
return AlipaySignature.rsaCheckV1((Map<String, String>) response.getRawSignObject(), merchant.getAppKey(), "UTF-8", "RSA2");
}
/**
* 根据订单号进行查询
* @param orderNo 内部订单编号
* @param merchant 商户信息
* @return
* @throws AlipayApiException
*/
@Override
public PayResult payOrderQuery(String orderNo, Merchant merchant) throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
merchant.getAppId(),merchant.getMchKey(),"json","GBK",merchant.getAppKey(),"RSA2");
AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderNo);
alipayTradeQueryRequest.setBizContent(bizContent.toString());
alipayTradeQueryRequest.putOtherTextParam("app_auth_token",merchant.getAppAuthToken());
String body = alipayClient.execute(alipayTradeQueryRequest).getBody();
JSONObject jsonObject = JSONObject.parseObject(body).getJSONObject("alipay_trade_query_response");
String code = jsonObject.getString("code");
if (!"10000".equals(code)){
throw new InternalRuntimeException("发起查询失败,原因["+code+":"+ jsonObject.getString("sub_msg")+"]");
}
String tradeStatus = jsonObject.getString("trade_status");
PayStatus payStatus;
switch (tradeStatus){
case "TRADE_FINISHED" :
payStatus = PayStatus.FINISHED;
break;
case "TRADE_SUCCESS" :
payStatus = PayStatus.PAID;
break;
case "TRADE_CLOSED" :
payStatus = PayStatus.CLOSE;
break;
default:
payStatus = PayStatus.PAYING;
}
return PayResult
.builder()
.outOrderNo(jsonObject.getString("trade_no"))
.orderNo(jsonObject.getString("out_trade_no"))
.payStatus(payStatus)
.build();
}
/**
* 时间戳转时间字符串
* @param timeStr 时间戳Long
* @return String 时间
*/
private Long timeFormat(String timeStr){
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
try {
return dateFormat.parse(timeStr).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 时间戳转时间字符串
* @param time 时间戳Long
* @return String 时间
*/
private String timeFormat(Long time){
return new SimpleDateFormat("yyyyMMddHHmmss").format(time);
}
}
3.支付宝实现层:
private static Merchant getMerchant(){
return Merchant.builder()
.appId("服务商的应用ID")
.mchKey("服务商的商户密钥")
.alipayAppKey("服务商的支付宝公钥")
.appAuthToken("商户授权成功后得到的TOKEN")
.build();
}
/**
* 二维码扫码支付
*/
@Test
public void testPay(){
Client client = Client.newInstance(PaySuper.ALIPAY);
PrePayRequest request = PrePayRequest.builder()
.amount(1)
.body("支付宝扫码支付测试")
.payMethod(PayMethod.ZFB_QR)
.notifyUrl("你的异步通知地址")
.build();
PrePayResponse response = client.prePay(request,getMerchant());
System.out.println(JSONUtil.toJsonStr(response));
}
/**
* 根据外部订单号进行查询
*/
@Test
public void testQuery(){
Client client = Client.newInstance(PaySuper.ALIPAY);
PayResult response = client.payOrderQuery("订单号",getMerchant());
System.out.println(JSONUtil.toJsonStr(response));
}
/**
* 注意:订单创建实际创建时间也为用户支付时间,非唤起收银台时间。
* @param
*/
@Test
public void wapPay(){
Client client = Client.newInstance(PaySuper.ALIPAY);
PrePayRequest request = PrePayRequest.builder()
.amount(1)
.body("支付宝网页支付测试")
.payMethod(PayMethod.ZFB_WEB)
.returnUrl("你的回调地址")
.notifyUrl("你的异步通知地址")
.build();
PrePayResponse prePayResponse = client.prePay(request, getMerchant());
System.out.println(JSONUtil.toJsonStr(prePayResponse));
}
@Test
public static void main(String[] args) throws AlipayApiException {
Client client = Client.newInstance(PaySuper.ALIPAY);
//回调的待验签字符串
String resultInfo = "异步通知地址返回的通知信息";
//对待签名字符串数据通过&进行拆分
String [] temp = resultInfo.split("&");
HashMap<String, String> map = new HashMap<String, String>();
//把拆分数据放在map集合内
for (int i = 0; i < temp.length; i++) {
String[] arr = temp[i].split("=", 2); //通过"="号分割成2个数据
String[] tempAagin = new String[arr.length]; //再开辟一个数组用来接收分割后的数据
for (int j = 0; j < arr.length; j++) {
tempAagin[j] = arr[j];
}
map.put(tempAagin[0], tempAagin[1]);
}
System.out.println(map);
//验签方法
PayNotify payNotify = PayNotify.builder().rawSignObject(map).build();
boolean signVerified = client.verifyPayNotify(payNotify,getMerchant());
if(signVerified){
// TODO 验签成功后
System.out.println("success");
}else{
System.out.println("fail");
}
}
(1)拼接授权URL:
public String AuthorizationUrl(String mchId,String outAppId) throws AlipayApiException {
// 第一步拼接授权地址,商户扫码进行授权
com.yuheng.payment.models.Merchant serviceMerchant = getServiceMerchant(outAppId);
AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
serviceMerchant.getAppId(),serviceMerchant.getMchKey(),"json","GBK",serviceMerchant.getAppKey(),"RSA2");
AlipayOpenAuthAppauthInviteCreateRequest request = new AlipayOpenAuthAppauthInviteCreateRequest();
String redirectUrl = getRedirectUrl();
JSONObject bizContent = new JSONObject();
bizContent.put("auth_app_id",serviceMerchant.getAppId());
bizContent.put("redirect_url",redirectUrl);
request.setBizContent(bizContent.toString());
AlipayOpenAuthAppauthInviteCreateResponse response = alipayClient.execute(request);
System.out.println("::::::::::::" + response.getTaskPageUrl());
return "https://openauth.alipay.com/oauth2/appToAppAuth.htm?app_id="
+ serviceMerchant.getAppId() // 第三方应用ID
+"&redirect_uri="
+"http://192.168.1.189:8005/api/merchant/redirect"
+"&state="
+mchId;
/*return "https://openauth.alipay.com/oauth2/appToAppAuth.htm?app_id="
+ appId // 第三方应用ID
+"&redirect_uri="
+"https://xqht.hnyuheng.net/";*/
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateAppAuthTokenById(String appAuthToken, String id) {
merchantRepository.addAppAuthTokenById(appAuthToken,id);
}
@Override
public MerchantDto selectServiceMerchant(String appId) {
Merchant merchant = merchantRepository.findByAppIdAndIsService(appId,true);
return merchantMapper.toDto(merchant);
}
private String getRedirectUrl(){
return RequestHolder.getAppUrl() + "/api/merchant/redirect/";
}
(2)商户点击链接或扫码成功授权后回调地址返回app_auth_token(回调地址一定要与第三方应用配置的回调地址一模一样)
授权回调接口:
package com.yuheng.modules.mch.rest;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayOpenAuthTokenAppRequest;
import com.alipay.api.response.AlipayOpenAuthTokenAppResponse;
import com.yuheng.annotation.AnonymousAccess;
import com.yuheng.exception.BadRequestException;
import com.yuheng.modules.mch.constant.PayType;
import com.yuheng.modules.mch.domain.MchApp;
import com.yuheng.modules.mch.service.MchAppService;
import com.yuheng.modules.mch.service.MerchantService;
import com.yuheng.modules.mch.service.dto.MerchantDto;
import com.yuheng.payment.constants.PaySuper;
import com.yuheng.payment.models.Merchant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 支付宝商户授权成功后返回地址
* @author liuzhaojun
* @createDate 2023-03-29 10:13
*/
@Slf4j
@RestController
@RequestMapping("/api/merchant/redirect")
@RequiredArgsConstructor
public class AlipayRedirectURLController {
// 服务环境
@Value("${alipay.serviceUrl}")
private String serviceUrl;
private final MerchantService merchantService;
private final MchAppService mchAppService;
@GetMapping
@AnonymousAccess
public ResponseEntity<Object> redirectURL(@RequestParam(value = "app_id") String outAppId, @RequestParam("state") String mchId, @RequestParam("source") String source, @RequestParam("app_auth_code") String appAuthCode) throws AlipayApiException {
// 商户同意授权之后支付宝回调参数--->用appAuthCode换取appAuthToken
Merchant serviceMerchant = getServiceMerchant(outAppId);
if (ObjectUtil.isNull(serviceMerchant)) throw new BadRequestException("未查到服务商信息");
AlipayClient alipayClient = new DefaultAlipayClient(serviceUrl,
outAppId,serviceMerchant.getMchKey(),"json","GBK",serviceMerchant.getAppKey(),"RSA2");
System.out.println("app_id=" + outAppId + ";source=" + source + ";app_auth_code=" +appAuthCode + ";state=" + mchId);
JSONObject bizContent = new JSONObject();
// 授权方式为通过授权码授权
bizContent.put("grant_type","authorization_code");
bizContent.put("code",appAuthCode);
AlipayOpenAuthTokenAppRequest request = new AlipayOpenAuthTokenAppRequest();
request.setBizContent(bizContent.toString());
AlipayOpenAuthTokenAppResponse response = alipayClient.execute(request);
if(response.isSuccess()){
System.out.println("Token:::::::::::" + response.getAppAuthToken());
System.out.println("RefreshToken:::::::::::" + response.getAppRefreshToken());
// 新增字段app_auth_token
merchantService.updateAppAuthTokenById(response.getAppAuthToken(),mchId);
// 将商户状态改为激活
merchantService.enable(mchId);
return new ResponseEntity<>(HttpStatus.OK);
} else {
String errorInfo = JSONObject.toJSONString(response);
log.error("调用失败" + errorInfo);
return new ResponseEntity<>(errorInfo,HttpStatus.BAD_REQUEST);
}
}
private Merchant getServiceMerchant(String outAppId){
// 此处appId是支付宝应用ID (应用表的外部应用ID) => 查出上游为支付宝且绑定了支付宝第三方应用的商户信息
MchApp mchApp = mchAppService.findByOutAppId(outAppId, PaySuper.ALIPAY.getName(), PayType.ALIPAY_THIRD.getCode());
// 查出服务商信息(以字段is_service为判断依据)
MerchantDto merchantDto = merchantService.selectServiceMerchant(mchApp.getId());
return new Merchant(mchApp.getOutAppId(), mchApp.getOutAppSecret(),
merchantDto.getOutMchId(),merchantDto.getOutMchKey(),"");
}
}
关于回调地址: