java 支付寶支付接入(手機web網頁支付)親測可用!

· 正式環境(需上線應用、簽約功能手機網站支付、企業支付寶

所需資質:

支付寶開發平臺配置圖片

· pom

        <!-- 支付寶相關 -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.71.ALL</version>
        </dependency>
        <!--StringUtils-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>

工具類:

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

/**
 * @author qingzhi
 * @date 2020/4/2 14:34
 */
public class SignUtil {

    /**
     * 連接Map鍵值對
     *
     * @param map
     *            Map
     * @param prefix
     *            前綴
     * @param suffix
     *            後綴
     * @param separator
     *            連接符
     * @param ignoreEmptyValue
     *            忽略空值
     * @param ignoreKeys
     *            忽略Key
     * @return 字符串
     */
    public static String joinKeyValue(Map<String, Object> map, String prefix, String suffix, String separator,
                                      boolean ignoreEmptyValue, String... ignoreKeys) {
        List<String> list = new ArrayList<String>();
        if (map != null) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                String value = String.valueOf(entry.getValue());
                if (StringUtils.isNotEmpty(key) && !ArrayUtils.contains(ignoreKeys, key)
                        && (!ignoreEmptyValue || StringUtils.isNotEmpty(value))) {
                    list.add(key + "=" + (value != null ? value : ""));
                }
            }
        }
        return (prefix != null ? prefix : "") + StringUtils.join(list, separator) + (suffix != null ? suffix : "");
    }

    /**
     * 把request請求參數轉換爲Map<String,String>
     * @param request 該請求
     * @return Map<String,String>格式的參數
     */
    public static Map<String,String> request2Map(HttpServletRequest request){
        Enumeration<String> names = request.getParameterNames();
        Map<String, String> resData = new HashMap<String, String>();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            resData.put(name, request.getParameter(name));
        }
        return resData;
    }

    /**
     * Bean轉map
     * @param bean 要轉的bean
     * @return 返回一個TreeMap
     */
    public static TreeMap<String, String> bean2TreeMap(Object bean) {
        TreeMap<String, String> requestMap = new TreeMap<String, String>();
        Class<?> cls = bean.getClass();
        Field[] fields = cls.getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                String key = fields[i].getName();
                fields[i].setAccessible(true);
                Object value = fields[i].get(bean);
                if ("sign".equals(key) || value == null || StringUtils.isEmpty(value.toString())) {
                    continue;
                }
                requestMap.put(key, value.toString());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return requestMap;
    }
}

·config


import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;

/**
 * 支付寶支付所需資質
 * @author qingzhi
 * @date 2020/4/2 14:56
 */
public class AlipayConfig {
    /**
     * 應用ID號()
     */
    public static String APP_ID = "";//你的應用appId
    /**
     * 你的私鑰
     */
    public static String APP_PRIVATE_KEY = "";
    /**
     * 編碼
     */
    public static String CHARSET = "UTF-8";
    /**
     * 支付寶公鑰
     */
    public static String ALIPAY_PUBLIC_KEY = "";
    /**
     * 支付寶網關地址
     */
    private static String GATEWAY = "https://openapi.alipay.com/gateway.do";//正式環境

    //private static String GATEWAY = "https://openapi.alipay.com/gateway.do";//沙箱環境
    /**
     * 成功付款回調
     */
    public static String PAY_NOTIFY = "http://c5129413.ngrok.io/alipay/pay";//驗籤
    /**
     * 支付成功回調
     */
    public static String REFUND_NOTIFY = "";//姑且沒用到
    /**
     * 前臺通知地址
     */
    public static String RETURN_URL = "http://c5129413.ngrok.io/alipay/index";//支付成功後返回哪個前端頁面
    /**
     * 參數類型
     */
    public static String PARAM_TYPE = "json";
    /**
     * 成功標識
     */
    public static final String SUCCESS_REQUEST = "TRADE_SUCCESS";
    /**
     * 交易關閉回調(當該筆訂單全部退款完畢,則交易關閉)
     */
    public static final String TRADE_CLOSED = "TRADE_CLOSED";
    /**
     * 支付寶開發平臺中的支付寶賬號(企業)
     */
    public static final String SELLER_ID = "";

    //簽名算法類型(根據生成私鑰的算法,RSA2或RSA)
    public static final String SIGNTYPE = "RSA2";
    /**
     * 支付寶請求客戶端入口
     */
    private volatile static AlipayClient alipayClient = null;

    /**
     * 不可實例化
     */
    private AlipayConfig(){};

    /**
     * 雙重鎖單例
     * @return 支付寶請求客戶端實例
     */
    public static AlipayClient getInstance(){
        if (alipayClient == null){
            synchronized (AlipayConfig.class){
                if (alipayClient == null){
                    alipayClient = new DefaultAlipayClient(GATEWAY,APP_ID,APP_PRIVATE_KEY,PARAM_TYPE,CHARSET,ALIPAY_PUBLIC_KEY,SIGNTYPE);
                }
            }
        }
        return alipayClient;
    }

}

·Controller

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Random;
/**
 * 支付寶支付(手機web網頁支付)
 * @author qingzhi
 * @date 2020/4/2 15:12
 */
@Controller
@RequestMapping("/alipay")
public class AlipayTrade {
    private Logger logger = LoggerFactory.getLogger(AlipayTrade.class);

    /**
     * web支付下單並支付(web支付在安卓中是可以直接喚醒支付寶APP的)
     * @return web支付的表單
     */
    @RequestMapping("/webPay")
    @ResponseBody
    public String TradeWapPayRequest(Map<String, String> sParaTemp){
        AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.RETURN_URL);//前臺回調地址
        alipayRequest.setNotifyUrl(AlipayConfig.PAY_NOTIFY);//成功付款回調
        // 待請求參數數組
        sParaTemp.put("seller_id",AlipayConfig.SELLER_ID);//收款方賬號
        sParaTemp.put("out_trade_no", getOrderIdByTime());//訂單號(唯一)注意(Test):這一單已付款,再掉起支付時會報此訂單已支付。那麼就得換個訂單號,索性搞個生成訂單號方法函數
        sParaTemp.put("total_amount","0.01");//訂單金額:0.01元,精準到分
        sParaTemp.put("subject","商品下單");//訂單標題
       // sParaTemp.put("product_code", "QUICK_WAP_PAY");//手機網頁支付
        sParaTemp.put("body", "牛奶泡澡,盡顯奢侈,嗷裏個嗷!只需0.01");//沒看到在哪顯示了,搞了再說。
        alipayRequest.setBizContent(JSON.toJSONString(sParaTemp));//
        String form = "";
        try {
            form = AlipayConfig.getInstance().pageExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            logger.error("支付寶構造表單失敗",e);
        }
        logger.debug("支付寶支付表單構造:"+form);
        return form;
    }

    /**
     * 申請退款
     * @param sParaTemp 退款參數
     * @return true成功,回調中處理
     * 備註:https://doc.open.alipay.com/docs/api.htm?spm=a219a.7629065.0.0.3RjsEZ&apiId=759&docType=4
     */
    public boolean tradeRefundRequest(Map<String, ?> sParaTemp) throws AlipayApiException {
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setReturnUrl(AlipayConfig.RETURN_URL);//前臺回調地址
        request.setNotifyUrl(AlipayConfig.REFUND_NOTIFY);//後臺回調地址
        // 待請求參數數組
        request.setBizContent(JSON.toJSONString(sParaTemp));
        AlipayTradeRefundResponse response = AlipayConfig.getInstance().execute(request);
        logger.debug("支付寶退貨結果:"+response.isSuccess());
        return response.isSuccess();
    }

    /**
     * 支付寶回調驗籤(也可在此處理業務邏輯)
     * @param request 回調請求
     * @return true成功
     * 備註:驗籤成功後,按照支付結果異步通知中的描述(二次驗籤接口,貌似稱爲歷史接口了)
     */
    @RequestMapping("/pay")
    @ResponseBody
    public boolean verifyNotify(HttpServletRequest request) throws AlipayApiException {
        Map<String,String> paranMap = SignUtil.request2Map(request);//工具類:把request轉爲map
        logger.debug("支付寶回調參數:"+paranMap.toString());
        boolean isVerify = false;
        //驗證
        if (AlipayConfig.SUCCESS_REQUEST.equals(paranMap.get("trade_status")) || AlipayConfig.TRADE_CLOSED.equals(paranMap.get("trade_status"))) {
            isVerify = AlipaySignature.rsaCheckV1(paranMap, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET,"RSA2"); //調用SDK驗證簽名,注意:RSA2類型,不填寫會報錯,驗籤異常錯誤,公鑰格式不正確。
        }
        logger.debug("支付寶驗簽結果"+isVerify);
        return isVerify;
    }

    @RequestMapping("/index")//templates頁面測試,甭管了。
    public ModelAndView index(HttpServletRequest rq){
        ModelAndView mv=new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

    /**
     * 生成訂單號,高併發情況下可加入訂單ID
     * @return
     */
    public static String getOrderIdByTime() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
        String newDate=sdf.format(new Date());
        String result="";
        Random random=new Random();
        for(int i=0;i<3;i++){
            result+=random.nextInt(10);
        }
        return newDate+result;
    }
}

頁面效果展示:

手機支付頁面展示      >>>      手機支付頁展示      >>>      手機支付頁展示

後端控制檯展示

完結。

  • “我覺得我就像集市上的魚,水分蒸發得差不多了。我時常覺得自己就要撐不下去,而你總是適時又吝嗇地灑點水給我,我便要再提着一口氣蹦噠兩下,作出生龍活虎的樣子給你看。我在前途未卜的痛苦裏週而復始,你是不要我死的,又偏不肯溫柔豢養我。”

  • 舔狗舔狗,越舔越有。

  • 嘎嘎嘎  ········

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章