文章目录
- 实现效果:
- 前提准备
- 支付流程
- 方案一
- 1. 导入依赖
- 2. 配置文件
- 3. 支付宝初始化
- 4. 唤起支付
- 方案二
- 1. 导入依赖
- 2. 唤起支付
实现效果:
前提准备
由于本文只是提及支付的流程及其一些相关知识点,所以前提数据自行准备,参考支付宝支付接口的调用
支付流程
举例说明:
如果平台只是给某商家使用,则全平台全部的收款都是一个,但是平台存在多个商家,各自收款,每个商家都存在不同的账户,则会出现多个收款方。
由于平台不确定有一个商家,即有几个收款方,所以,提供了两种方案唤起支付宝支付,
如果平台只有一个收款方,则使用方案一:单收款方;
如果平台存在多个收款方,则使用方案二:多收款方;
方案一
1. 导入依赖
<!--aliPay-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.2.0</version>
</dependency>
2. 配置文件
alipay.appId=2021000116671219
# 商户私钥
alipay.privateKey=参数自备
# 支付宝公钥
alipay.publicKey=参数自备
# 服务器异步通知页面路径 ,需要公网能访问到。
alipay.notifyUrl=回调地址,需要根据自己实际的回调地址填写
# 页面跳转同步通知页面路径 需要公网能访问到。支付完成之后页面跳转url
alipay.returnUrl=https://www.baidu.com
# 签名方式
alipay.signType=RSA2
# 字符编码格式
alipay.charset=utf-8
# 支付宝网关
alipay.gateway=openapi.alipaydev.com
# 支付宝日志
alipay.logPath="C:\\"
3. 支付宝初始化
创建一个类,实现ApplicationRunner,这里面的值都是从配置文件获取。
代码:
package com.mallshop.init;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* 项目初始化
*
* @author
* @date 2020-09-08
*/
@Component
public class ProjectInit implements ApplicationRunner {
//应用id
@Value("${alipay.appId}")
private String appId;
//私钥
@Value("${alipay.privateKey}")
private String privateKey;
//公钥
@Value("${alipay.publicKey}")
private String publicKey;
//支付宝网关
@Value("${alipay.gateway}")
private String gateway;
//支付成功后的接口回调地址,不是回调的友好页面,不要弄混了
@Value("${alipay.notifyUrl}")
private String notifyUrl;
/**
* 项目初始化事件
*
*/
@Override
public void run(ApplicationArguments args) throws Exception {
//初始化支付宝SDK,使用Factory,设置客户端参数,只需设置一次,即可反复使用各种场景下的API Client
Factory.setOptions(getOptions());
System.out.println("**********支付宝SDK初始化完成**********");
}
private Config getOptions() {
//这里省略了一些不必要的配置,可参考文档的说明
Config config = new Config();
config.protocol = "https";
config.gatewayHost = this.gateway;
config.signType = "RSA2";
config.appId = this.appId;
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
config.merchantPrivateKey = this.privateKey;
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
config.alipayPublicKey = this.publicKey;
//可设置异步通知接收服务地址(可选)
config.notifyUrl = notifyUrl;
return config;
}
}
4. 唤起支付
创建一个接口,调用唤起支付宝支付,注意:唤起支付页面可能存在多种情况,
- 当面付 FaceToFace
- 电脑网站 Page
- 手机网站 Wap
- App支付 App
实际是哪一个支付环境使用对应的方法,具体的方法参数等参考支付宝服务端API的详细参数说明
代码:
/**
* <p>
* 前端控制器
* </p>
*
* @author
* @since 2021-05-18
*/
@RestController
@CrossOrigin
@RequestMapping("/card")
public class CardController {
@Autowired
CardService cardService;
@Autowired
CardRecordService cardRecordService;
//支付成功后要跳转的页面
@Value("${alipay.returnUrl}")
private String returnUrl;
// private Map<String, String> map = new HashMap<>();
/**
* 充值
*/
@PostMapping(value = "/addMoney", produces = {"text/html;charset=UTF-8"})
public Object add(CardRecord cardRecord) throws Exception {
cardRecord.setOrderNo(OrderUtil.getOrderNo());
cardRecord.setType("1");
cardRecordService.save(cardRecord);
//手机网站
// 参数一:订单名
//参数二:订单号
//参数三:金额
//参数四:用户付款中途退出返回商户网站的地址
//参数五:返回的url
//AlipayTradeWapPayResponse response = Factory.Payment.Wap().pay("充值", cardRecord.getOrderNo(), cardRecord.getMoney().toString(), "", returnUrl);
//电脑网站支付
// 参数一:订单名
//参数二:订单号
//参数三:金额
//参数四:返回的url
AlipayTradePagePayResponse response = Factory.Payment.Page().pay("充值", cardRecord.getOrderNo(), cardRecord.getMoney().toString(), returnUrl);
return response.body;
}
/**
* 充值成功进行的回调,根据自己业务逻辑
* 唤起支付的参数orderNo会在回调中返回,获取到订单号关联出订单。
*/
@PostMapping("/fallMoney")
public Result add(HttpServletRequest request) {
//支付宝回调参数
Map<String, String> map = new HashMap<>();
Enumeration<String> paramsName = request.getParameterNames();
while (paramsName.hasMoreElements()) {
String key = paramsName.nextElement();
String value = request.getParameter(key);
map.put(key, value);
System.out.println("接收到的参数---->" + key + "/values:" + value);
}
//接收到订单号和金额
String orderNo = String.valueOf(map.get("out_trade_no"));
String money = String.valueOf(map.get("buyer_pay_amount"));
BigDecimal money1 = new BigDecimal(money);
System.out.println("接收到的参数订单金额---->" + map.get("buyer_pay_amount") + "/订单号" + map.get("out_trade_no"));
//根据订单号查询carRecord并修改支付状态
CardRecord cardRecord1 = cardRecordService.getOne(Wrappers.<CardRecord>lambdaQuery().eq(CardRecord::getOrderNo, orderNo));
cardRecord1.setPayStatus("1");
cardRecordService.updateById(cardRecord1);
//获取到当前卡和余额
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("user_id", cardRecord1.getUserId());
Card card = cardService.getOne(wrapper);
System.out.println("当前用户卡信息---->" + card);
if (card == null) {
return Result.fail("数据异常");
}
//更新卡余额
card.setBalance(card.getBalance().add(money1));
cardService.updateById(card);
return Result.success(null);
}
方案二
1. 导入依赖
<!--aliPay-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.2.0</version>
</dependency>
2. 唤起支付
/**
* <p>
* 前端控制器
* </p>
*
* @author
* @since 2021-05-18
*/
@RestController
@CrossOrigin
@RequestMapping("/card")
public class CardController {
@Autowired
CardService cardService;
@Autowired
CardRecordService cardRecordService;
//支付成功后要跳转的页面
@Value("${alipay.returnUrl}")
private String returnUrl;
// private Map<String, String> map = new HashMap<>();
/**
* 充值
*/
@PostMapping(value = "/addMoney", produces = {"text/html;charset=UTF-8"})
public Object add(CardRecord cardRecord) throws Exception {
cardRecord.setOrderNo(OrderUtil.getOrderNo());
cardRecord.setType("1");
cardRecordService.save(cardRecord);
//写法一:不封装,写死的参数
MultipleFactory multipleFactory=new MultipleFactory();
multipleFactory.setOptions(getOptions());
AlipayTradePagePayResponse response = multipleFactory.Page()
//.agent()这个参数是指定appAuthToken
//.agent("")
//调用asyncNotify扩展方法,可以为每次API调用,设置独立的异步通知地址,此处设置的异步通知地址的优先级高于全局Config中配置的异步通知地址
.asyncNotify(returnUrl)
//参数一:订单名 参数二:订单号 参数三:金额 参数四:返回的url
.pay("充值", cardRecord.getOrderNo(), cardRecord.getMoney().toString(), returnUrl);
//写法二:封装,根据店铺id获取
MultipleFactory multipleFactory = AliPayConfiguration.getFactory(cardRecord.getShopId);
AlipayTradePagePayResponse response = multipleFactory.Page()
//.agent()这个参数是指定appAuthToken
//.agent("")
//调用asyncNotify扩展方法,可以为每次API调用,设置独立的异步通知地址,此处设置的异步通知地址的优先级高于全局Config中配置的异步通知地址
.asyncNotify(returnUrl)
//参数一:订单名 参数二:订单号 参数三:金额 参数四:返回的url
.pay("充值", cardRecord.getOrderNo(), cardRecord.getMoney().toString(), returnUrl);
return response.body;
}
/**
* 充值成功进行的回调
*/
@PostMapping("/fallMoney")
public Result add(HttpServletRequest request) {
//支付宝回调参数
Map<String, String> map = new HashMap<>();
Enumeration<String> paramsName = request.getParameterNames();
while (paramsName.hasMoreElements()) {
String key = paramsName.nextElement();
String value = request.getParameter(key);
map.put(key, value);
System.out.println("接收到的参数---->" + key + "/values:" + value);
}
//接收到订单号和金额
String orderNo = String.valueOf(map.get("out_trade_no"));
String money = String.valueOf(map.get("buyer_pay_amount"));
BigDecimal money1 = new BigDecimal(money);
System.out.println("接收到的参数订单金额---->" + map.get("buyer_pay_amount") + "/订单号" + map.get("out_trade_no"));
//根据订单号查询carRecord并修改支付状态
CardRecord cardRecord1 = cardRecordService.getOne(Wrappers.<CardRecord>lambdaQuery().eq(CardRecord::getOrderNo, orderNo));
cardRecord1.setPayStatus("1");
cardRecordService.updateById(cardRecord1);
//获取到当前卡和余额
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("user_id", cardRecord1.getUserId());
Card card = cardService.getOne(wrapper);
System.out.println("当前用户卡信息---->" + card);
if (card == null) {
return Result.fail("数据异常");
}
//更新卡余额
card.setBalance(card.getBalance().add(money1));
cardService.updateById(card);
return Result.success(null);
}
private Config getOptions() {
//这里省略了一些不必要的配置,可参考文档的说明
Config config = new Config();
config.protocol = "https";
config.gatewayHost = "openapi.alipaydev.com";
config.signType = "RSA2";
config.appId = "参数自备";
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
config.merchantPrivateKey = "参数自备";
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
config.alipayPublicKey = "参数自备";
//可设置异步通知接收服务地址(可选)
config.notifyUrl = "http://eh6jnw.natappfree.cc/card/fallMoney";
return config;
}
附上写法二的AliPayConfiguration:
package com.mallshop.config;
import com.alipay.easysdk.factory.MultipleFactory;
import com.alipay.easysdk.kernel.Config;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
/**
* 获取支付
* @author lenovo
* @date 2023/2/1
*/
@Slf4j
@Configuration
public class AliPayConfiguration {
private static PayConfigService payConfigService;
@Autowired
public AliPayConfiguration(PayConfigService payConfigService) {
this.payConfigService = payConfigService;
}
/**
* 获取Factory
* @return
*/
public static MultipleFactory getFactory(String shopId) throws Exception {
MultipleFactory multipleFactory = new MultipleFactory();
multipleFactory.setOptions(getOptions(shopId));
return multipleFactory;
}
private static Config getOptions(String shopId) throws Exception {
PayConfig payConfig = payConfigService.getOne(Wrappers.<PayConfig>query().lambda()
.eq(PayConfig::getShopId, shopId));
if(payConfig == null){
throw new Exception("当前店铺没有配置支付宝");
}
Config config = new Config();
config.protocol = "https";
config.gatewayHost = "openapi.alipay.com";
config.signType = "RSA2";
config.appId = payConfig.getAppId();
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
config.merchantPrivateKey = payConfig.getMchKey();
//注:证书文件路径支持设置为文件系统中的路径或CLASS_PATH中的路径,优先从文件系统中加载,加载失败后会继续尝试从CLASS_PATH中加载
//请填写您的应用公钥证书文件路径,
config.merchantCertPath = payConfig.getPrivateCertPath();
//请填写您的支付宝公钥证书文件路径,
config.alipayCertPath = payConfig.getPrivateKeyPath();
//请填写您的支付宝根证书文件路径,
config.alipayRootCertPath = payConfig.getKeyPath();
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// config.alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIBIjANBg... -->";
return config;
}
}