一. APP支付
APP支付适用于商家在 App 应用中集成支付宝支付功能。 商家APP调用支付宝提供的 SDK,SDK 再调用支付宝APP内的支付模块。如果用户已安装支付宝 APP,商家 APP 会跳转到支付宝中完成支付,支付完后跳回到商家APP内,最后展示支付结果。如果用户没有安装支付宝 APP,商家 APP 内会调起支付宝网页支付收银台,用户登录支付宝账户,支付完后展示支付结果。 目前支持手机系统有:iOS(苹果)、Android(安卓)。
【用户已安装支付宝支付流程】
◆ 用户在商家 App 中选择商品下单、确认购买,进入支付环节,选择支付宝,用户点击确认支付,如图1
◆ 进入到支付宝页面,调起支付宝支付,出现确认支付界面,如图2
◆ 用户确认收款方和金额,点击立即支付后出现输入密码界面,如图3
◆ 输入正确密码后,支付宝端显示支付结果,如图 4; 5自动回跳到商家 App 中,商家根据付款结果个性化展示订单处理结果,如图 5。
【用户未安装支付宝支付流程】
◆ 用户在商家 App 中选择商品下单、确认购买,进入支付环节,选择支付宝,用户点击确认支付,如图 6;
◆ 用户未安装支付宝客户端,则调起支付宝网页支付收银台,用户登录支付宝账户,如图 7;
◆ 登录成功后,进入确认付款页面,如图 8;
◆ 用户点击确认付款,进入支付密码页面,如下图 9; 5.用户输入密码,完成支付,展示支付结果,如图 10
【接入前准备】
参考官网:https://opendocs.alipay.com/open/204/105297
【Alipay通用工具类】
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alipay.api.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransOrderQueryRequest;
import com.alipay.api.request.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayFundTransOrderQueryResponse;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.maiji.cloud.request.shopingmall.AlipayRefundReqData;
import com.maiji.cloud.request.shopingmall.AlipayRefundReqData.RefundReqInner;
import org.apache.commons.lang.StringUtils;
public class AlipayUtil {
public static final String APP_ID = "";
// 支付宝支付私钥
public static final String PRIVATE_KEY = "";
// 支付宝支付公钥
public static final String PUBLIC_KEY = "";
// 支付成功回调url
public static final String NOTIFY_URL = "";
public static String createSign(Map parameters) throws Exception {
List<String> keys = new ArrayList<String>(parameters.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
Object value = parameters.get(key);
// value = URLEncoder.encode(value, "UTF-8");
if(StringUtils.isNotBlank(parameters.get(key)+"") && value != null) {
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
}
return prestr;
}
public static String createSignValues(Map parameters) throws Exception {
List<String> keys = new ArrayList<String>(parameters.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
Object value = parameters.get(key);
if(StringUtils.isNotBlank(parameters.get(key)+"") && value != null) {
value = URLEncoder.encode(value+"", "UTF-8");
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
}
return prestr;
}
public static AlipayFundTransToaccountTransferResponse alipayWithdraw(String ioUuid,String realName,String account,Double amount) throws AlipayApiException{
Map<String, Object> map = new HashMap<String, Object>();
map.put("out_biz_no", ioUuid);
map.put("payee_type", "ALIPAY_LOGONID");
map.put("payee_account", account);
map.put("payee_real_name", realName);
map.put("amount", amount);
map.put("remark", "提现");
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do", AlipayUtil.APP_ID,
AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY,"RSA2");
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
request.setBizContent(JSON.toJSONString(map));
AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
return response;
}
public static AlipayTradeRefundResponse alipayRefund(String orderNo,String payId, Double refundMoney) throws Exception {
String nowTime = TimeUtil.date2String(new Date(), null);
AlipayRefundReqData aliPayResData = new AlipayRefundReqData();
aliPayResData.setApp_id(AlipayUtil.APP_ID);
aliPayResData.setCharset("utf-8");
aliPayResData.setMethod("alipay.trade.refund");
aliPayResData.setSign_type("RSA2");
aliPayResData.setTimestamp(nowTime);
aliPayResData.setVersion("1.0");
RefundReqInner aliPayResInner = aliPayResData.dataInstance();
aliPayResInner.setOut_trade_no(orderNo);
aliPayResInner.setRefund_amount(refundMoney);
aliPayResInner.setTrade_no(payId);
aliPayResInner.setOut_request_no(orderNo);
Map paramMap = WXUtil.objectToMap(aliPayResData);
paramMap.remove("biz_content");
paramMap.put("biz_content", JSON.toJSON(aliPayResInner));
String signString = AlipayUtil.createSign(paramMap);
String sign = AlipaySignature.rsaSign(signString, AlipayUtil.PRIVATE_KEY, "utf-8", "RSA2");
aliPayResData.setSign(sign);
paramMap.put("sign", sign);
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID,AlipayUtil.PRIVATE_KEY,"json","utf-8",AlipayUtil.PUBLIC_KEY,"RSA2");
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.setBizContent( JSON.toJSONString(aliPayResInner));
AlipayTradeRefundResponse response = alipayClient.execute(request);
if (!"Y".equals(response.getFundChange())) {
throw new Exception("调用退款接口退款未成功");
}
return response;
}
}
【app支付接口2.0】
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class AlipayDto{
@ApiModelProperty(value="总金额")
private double totalMoney;
@ApiModelProperty(value="订单号")
private String out_trade_no;
}
@PostMapping("alipay")
public BaseDataResDto<AlipayTradeAppPayResponse> alipay(@RequestBody AlipayDto param, @RequestHeader("token") String token) throws Exception {
return capitalMainLogService.alipay(param, token);
}
@Override
public BaseDataResDto<AlipayTradeAppPayResponse> alipay(AlipayDto param, String token) throws Exception {
// 实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", AlipayUtil.APP_ID,
AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY, "RSA2");
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject("商品支付");
model.setBody("购买商品-请尽快付款");
model.setOutTradeNo(param.getOut_trade_no());
model.setTotalAmount(param.getTotalMoney() + "");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl(AlipayUtil.NOTIFY_URL);
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
return new BaseDataResDto(Status.ERROR);
}
【支付宝支付成功回调】
对于App支付产生的交易,支付宝会根据原始支付 API 中传入的异步通知地址 notify_url,通过 POST 请求的形式将支付结果作为参数通知到商户系统,详见官方文档:https://opendocs.alipay.com/open/204/105301
@PostMapping("alipayCallBack")
public String alipayCallBack(HttpServletRequest request) {
Map<String, String> parameters = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
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"); 这段代码在出现乱码时使用
parameters.put(key, valueStr);
}
return capitalMainLogService.alipayCallBack(parameters);
}
@Override
public String alipayCallBack(Map<String, String> param) throws AlipayApiException{
boolean signVerified = AlipaySignature.rsaCheckV1(param, AlipayUtil.PUBLIC_KEY, param.get("charset"), param.get("sign_type")); // 调用SDK验证签名
if (!signVerified) return "failure";
ShopingOrder shopingOrder = orderDao.getByOrderNo(param.get("out_trade_no"));
// 已经付款
if (Arrays.asList(1, 2, 3, 5, 6).contains(shopingOrder.getStatus())) {
return "success";
}
// 判断金额是否一致
if(!StringUtils.equals(shopingOrder.getAmount(), param.get("total_amount"))){
return "failure"
}
if ("TRADE_SUCCESS".equals( param.get("trade_status"))) {
// 具体业务逻辑,如修改订单状态,流水记录,积分等...
}
return "success";
}
【查询订单】
该接口提供所有支付宝支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。 需要调用查询接口的情况: 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; 调用支付接口后,返回系统错误或未知交易状态情况; 调用alipay.trade.pay,返回INPROCESS的状态; 调用alipay.trade.cancel之前,需确认支付状态。
@RestController
public class AlipayController {
@PostMapping("/alipayOrderQuery")
public BaseDataResDto<Map<String, Object>> alipayOrderQuery(@RequestBody BaseReqDto<String> baseReqDto){
String out_trade_no = baseReqDto.getData(); // 商户订单号
// 订单支付时传入的商户订单号,和支付宝交易号不能同时为空,trade_no,out_trade_no如果同时存在优先取trade_no
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID, AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY, "RSA2");
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+"\"trade_no\":\""+ "" +"\"}");
try{
AlipayTradeQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return new BaseDataResDto(Status.ERROR);
}
}
【退款】
当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,支付宝将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。 交易超过约定时间(签约时设置的可退款时间)的订单无法进行退款 支付宝退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
这里的交易退款接口是指统一收单交易退款接口(alipay.trade.refund),统一收单交易退款接口本身接口不支持设置notify_url参数,因此退款导致触发的异步通知是发送到支付接口中设置的notify_url。
交易退款接口是否会触发异步通知:https://opensupport.alipay.com/support/helpcenter/193/201602484851
@Override
public BaseResDto executeRefund(String orderRefundId) throws Exception{
ShoppingOrderRefundEntity shoppingOrderRefund = shopingOrderRefundService.selectById(orderRefundId).setRefundMiddleTime(new Date()).setStatus(3);//退款中
String orderId = shoppingOrderRefund.getOrderId();
ShopingOrder shopingOrder = shopingOrderMapper.selectById(orderId) .setRefundStatus(3);//退款中
Double refundMoney = shoppingOrderRefund.getRefundMoney();
Double amount = shopingOrder.getAmount();
if (refundManey > amount)
return BaseResDto.baseResDto(Status.ERROR, "退款金额错误!");
AlipayTradeRefundResponse response = AlipayUtil.alipayRefund(shopingOrder.getOrderNo(), shopingOrder.getPayId(), refundManey);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
// 下面接入自己的业务逻辑...
return new BaseResDto(Status.SUCCESS);
}
【查询退款结果】
官方文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query
@RestController
public class AliPayController {
@PostMapping("/alipayRefundQuery")
public BaseDataResDto<Map<String,Object>> alipayRefundQuery(@RequestBody Map<String,Object> param){
Object outTradeNo = param.get("outTradeNo");
Object outRequestNo = param.get("outRequestNo");
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID, AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY, "RSA2");
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
request.setBizContent("{\"out_trade_no\":\""+ outTradeNo +"\","
+"\"out_request_no\":\""+ outRequestNo +"\"}");
try{
AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
} catch (AlipayApiException e){
e.printStackTrace();
}
return new BaseDataResDto(Status.ERROR);
}
}
【转到到支付宝账户】(提现)
官方文档:https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer
public static AlipayFundTransToaccountTransferResponse alipayWithdraw(String orderNo,String realName,String account,Double amount) throws AlipayApiException{
Map<String, Object> map = new HashMap<String, Object>();
map.put("out_biz_no", orderNo);
map.put("payee_type", "ALIPAY_LOGONID");
map.put("payee_account", account);
map.put("payee_real_name", realName);
map.put("amount", amount);
map.put("remark", "提现");
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do", AlipayUtil.APP_ID,
AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY,"RSA2");
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
request.setBizContent(JSON.toJSONString(map));
AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
return response;
}
public static void main (String[] args) throws Exception {
AlipayFundTransToaccountTransferResponse response = AlipayUtil.alipayWithdraw(UUID_MD5.getUUID(), "姓名", "手机号码",0.1 );
System.out.println(JSON.toJSONString(response));
if(response.isSuccess()){
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
}
二. 电脑网站支付
@RestController
public class AlipayPC {
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID,AlipayUtil.PRIVATE_KEY,"json","utf-8",AlipayUtil.PUBLIC_KEY,"RSA2");
@PostMapping("/alipayPC")
public String alipayPC() throws Exception{
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
/** 同步通知,支付完成后,支付成功页面*/
alipayRequest.setReturnUrl("自定义");
/** 异步通知,支付完成后,需要进行的异步处理*/
alipayRequest.setNotifyUrl(AlipayUtil.NOTIFY_URL);
String subject = "测试付款";
alipayRequest.setBizContent("{\"out_trade_no\":\""+ 1234567 +"\","
+ "\"total_amount\":\""+ 0.1 +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ "商品费用,请尽快付款" +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
/**转换格式 (这里生成一个表单,会自动提交) */
return alipayClient.pageExecute(alipayRequest).getBody().replace('\"','\'').replace('\n',' ');
}
}
将接口的form表单返给前端,前端直接新开一个页面即可跳到支付宝网关支付页面 ~
电脑网站支付,手机网站支付,APP支付,三种支付方式的查询订单,退款,查询退款等的API是一致的,没什么特殊情况,支付成功回调也是一致的,完全可以配成一个异步回调 ~
最后附上支付宝沙箱环境的链接:https://opendocs.alipay.com/open/200/105311