APP第三方支付的实现-支付宝篇
关于支付宝的支付实现,在支付宝开放平台已经有了很完善的开发流程( app支付api)、(app支付demo),最近公司的项目需要使用用到app支付,作为支付的初次体验者,以下把开发的流程以及开发过程中遇到的问题记录下来。
开发准备
1. 支付宝沙箱环境
2.配置沙箱账号,下载沙箱测试版支付宝app
3.下载秘钥生成工具,同时配置到沙箱
4.下载客户端调试工具
代码实现
引入支付包支付 jar 包
<!--alipay-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.5.0.ALL</version>
</dependency>
从沙箱环境获取 app_id、支付宝公钥、商户私钥
从官方demo中可以获取到对应的基础配置类
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2020-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
@Configuration
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "";
// 商户私钥,您的PKCS8格式RSA2私钥
public static String merchant_private_key = "";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "";
// 签名方式(自己生成秘钥时使用的加密方式,官方推荐为RSA2)
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "utf-8";
// 支付宝网关(沙箱环境)
// 正式环境网关为 https://mapi.alipay.com/gateway.do
public static String gatewayUrl = "https://mapi.alipaydev.com/gateway.do";
配置类中需要注意的几点:
- 签名方式一定为以及生成秘钥时使用的签名方式
- 支付宝测试环境与正式环境的网关有所不同
- 服务器异步回调url 一定为外网可访问的地址(localhost 是访问不到的)。有服务器的可以写自己或公司服务器的公网IP。如果本地回调测试可以进行一下内网穿透,来让外网访问本地服务。
附:NETAPP内网穿透教程
下载内网穿透客户端
购买免费的隧道后只需要配置自己的端口号即可
根据教程实现内网穿透对应的Forwarding就是可访问的内网的域名。回调地址写入 域名/xxx/xxx 即可访问本地方法的回调
支付接口
@RequestMapping(value = "/pay", method = RequestMethod.POST) // 参数+body 商品描述 type 支付设置
@ResponseBody
public Msg payController(ServletRequest request, HttpServletResponse response, @RequestBody PayParameters payParameters) throws IOException {
//这里你可以做一些存表操作..具体根据你们自己的业务来操作... 比如生成预支付订单
String orderString = "";//这个字符串是用来返回给前端的
// 获取用户的支付宝账号信息
String appId = AlipayConfig.app_id // appId
String merchantPrivateKey = AlipayConfig.merchant_private_key // 商户私钥
String publicKey = AlipayConfig.alipay_public_key // 支付包公钥
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, appId, merchantPrivateKey, "json", AlipayConfig.charset, publicKey, AlipayConfig.sign_type);
//设置请求参数
AlipayTradeAppPayRequest alipayRequest = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
// 信息描述 自定义
model.setBody("购买" + payParameters.getCommodity());
// 商品名称 自定义
model.setSubject("购买" + payParameters.getCommodity());
//订单编号 根据用户id生成订单编号
model.setOutTradeNo("自己生成订单编号");
//交易超时时间
model.setTimeoutExpress("30m");
//支付金额 只能保留两位小数
model.setTotalAmount(money.toString());
//销售产品码(固定值) //这个不做多解释..看文档api接口参数解释
model.setProductCode("QUICK_MSECURITY_PAY");
alipayRequest.setBizModel(model);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url); // 异步回调地址
// 这里和普通的接口调用不同,使用的是sdkExecute
//返回支付宝订单信息(预处理)
AlipayTradeAppPayResponse alipayTradeAppPayResponse = null;
try {
alipayTradeAppPayResponse = alipayClient.sdkExecute(alipayRequest);
} catch (AlipayApiException e) {
e.printStackTrace();
}
//就是orderString 可以直接给APP请求,无需再做处理。
orderString = alipayTradeAppPayResponse.getBody();
return Msg.success().add("orderString", orderString);
}
支付接口调用之后会生成一个加密的 orderString 拿着这个orderString 去之前下载的客服端调试中测试,看是否能唤起沙箱支付宝 并付款。
如付款成功后,支付宝会异步回调我们定义的接口,回调接口如下:
@RestController
public class AliNotify {
private static final Logger log = LoggerFactory.getLogger(AliNotify.class);
/**
* 支付宝支付成功后.异步请求该接口
*
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value = "/aliNotify", method = RequestMethod.POST)
@Transactional(rollbackFor = Exception.class) //开启事务
public String aliNotify(HttpServletRequest request) throws IOException {
//获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
//2.封装必须参数
String out_trade_no = request.getParameter("out_trade_no"); // 商户订单号
String orderType = request.getParameter("body"); // 订单内容
String tradeStatus = request.getParameter("trade_status"); //交易状态
// 3.签名验证
boolean flag = false;
String appId = ; // appId
String merchantPrivateKey = ; // 商户私钥
String publicKey = ; // 支付包公钥
try {
flag = AlipaySignature.rsaCheckV1(params, publicKey, AlipayConfig.charset, AlipayConfig.sign_type);
} catch (AlipayApiException e) {
e.printStackTrace();
}
//4.对验签结果进行处理
if (flag) {
if (tradeStatus.equals("TRADE_SUCCESS")) { //只处理支付成功的订单: 修改交易表状态,支付成功
//此处可以做一些支付成功后的操作,比如修改订单状态为已支付
return "success";
}
}
return "fail";
}
}
支付流程已完成
代码中的具体使用到的实体类 如下
// 支付入参 根据自己实际需求来使用 数据都可模拟
public class PayParameters {
// 支付类型 --前端传入 微信 or 支付包
private Integer type;
// 商品姓名 --前端传入
private String productName;
// 商品描述
private String body;
// 订单编号
private String orderNum;
// 付款金额 --在接口中根据实际计算金额 数据可模拟
private BigDecim alpayAmount;
// 购买类型 --前端传入
private String commodity;
// 购买规格 --前端传入
private Integer commoditySpecifications;
// 销售产品码 固定
private String productCode;
// get set 方法
Msg 定义的一个返回对象
//专门用来返回json数据的通用类
public class Msg {
//状态码100-成功 200-失败
private int code;
private String msg;
private Map<String, Object> extend = new HashMap<String, Object>();
public static Msg success() {
Msg result = new Msg();
result.setCode(100);
result.setMsg("处理成功");
return result;
}
public static Msg NoImpact() {
Msg result = new Msg();
result.setCode(300);
result.setMsg("处理结束");
return result;
}
public static Msg fail() {
Msg result = new Msg();
result.setCode(200);
result.setMsg("处理失败");
return result;
}
public Msg() {
}
public Msg(int code, String msg, Map<String, Object> extend) {
this.code = code;
this.msg = msg;
this.extend = extend;
}
public Msg add(String key, Object value) {
this.getExtend().put(key, value);
return this;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getExtend() {
return extend;
}
public void setExtend(Map<String, Object> extend) {
this.extend = extend;
}
}
以上就是App支付唤醒支付包支付的全流程。