Java - 支付宝支付

一. 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通用工具类】

package com.maiji.cloud.utils;

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

猜你喜欢

转载自blog.csdn.net/AkiraNicky/article/details/106850253