支付寶電腦網站支付接口用沙箱簡單測試


我使用springboot+jsp做的簡單測試

官方電腦網站支付文檔https://docs.open.alipay.com/270

首先加入支付寶接口的依賴、配置文件和工具類

<!-- 支付寶支付接口依賴 -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.9.13.ALL</version>
</dependency>

添加請求時要用的參數配置文件alipay.properties

#應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
alipay.app_id=
#商戶私鑰,您的PKCS8格式RSA2私鑰
alipay.merchant_private_key=
#支付寶公鑰
alipay.alipay_public_key=
#服務器異步通知頁面路徑
alipay.notify_url=
#頁面跳轉同步通知頁面路徑
alipay.return_url=
#簽名方式
alipay.sign_type=RSA2
#字符編碼格式
alipay.charset=utf-8
#支付寶網關
alipay.gatewayUrl=https://openapi.alipaydev.com/gateway.do
alipay.log_path=C:\

請到支付寶沙箱中找相關參數填上(異步通知和同步通知可以先不填,后面慢慢講)

開發者中心-控制台-開發服務-研發服務-沙箱

在啟動類加載配置文件

@SpringBootApplication
@PropertySource({"classpath:alipay.properties"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

添加AlipayConfig類

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.io.FileWriter;
import java.io.IOException;

/**
 * 類名:AlipayConfig 功能:基礎配置類 詳細:設置帳戶有關信息及返回路徑
 */
@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;

	// 簽名方式
	public static String sign_type;

	// 字符編碼格式
	public static String charset;

	// 支付寶網關
	public static String gatewayUrl;

	// 支付寶網關
	public static String log_path;


	public String getApp_id() {
		return app_id;
	}

	@Value("${alipay.app_id}")
	public void setApp_id(String app_id) {
		AlipayConfig.app_id = app_id;
	}

	public String getMerchant_private_key() {
		return merchant_private_key;
	}

	@Value("${alipay.merchant_private_key}")
	public void setMerchant_private_key(String merchant_private_key) {
		AlipayConfig.merchant_private_key = merchant_private_key;
	}

	public String getAlipay_public_key() {
		return alipay_public_key;
	}

	@Value("${alipay.alipay_public_key}")
	public void setAlipay_public_key(String alipay_public_key) {
		AlipayConfig.alipay_public_key = alipay_public_key;
	}

	public String getNotify_url() {
		return notify_url;
	}

	@Value("${alipay.notify_url}")
	public void setNotify_url(String notify_url) {
		AlipayConfig.notify_url = notify_url;
	}

	public String getReturn_url() {
		return return_url;
	}

	@Value("${alipay.return_url}")
	public void setReturn_url(String return_url) {
		AlipayConfig.return_url = return_url;
	}

	public String getSign_type() {
		return sign_type;
	}

	@Value("${alipay.sign_type}")
	public void setSign_type(String sign_type) {
		AlipayConfig.sign_type = sign_type;
	}

	public String getCharset() {
		return charset;
	}

	@Value("${alipay.charset}")
	public void setCharset(String charset) {
		AlipayConfig.charset = charset;
	}

	public String getGatewayUrl() {
		return gatewayUrl;
	}

	@Value("${alipay.gatewayUrl}")
	public void setGatewayUrl(String gatewayUrl) {
		this.gatewayUrl = gatewayUrl;
	}

	public String getLog_path() {
		return log_path;
	}

	@Value("${alipay.log_path}")
	public void setLog_path(String log_path) {
		AlipayConfig.log_path = log_path;
	}

	/**
	 * 寫日志,方便測試(看網站需求,也可以改成把記錄存入數據庫)
	 *
	 * @param sWord 要寫入日志里的文本內容
	 */
	public static void logResult(String sWord) {
		FileWriter writer = null;
		try {
			writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
			writer.write(sWord);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

添加AlipayUtil工具類

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.example.config.AlipayConfig;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 支付寶接口工具類
 *
 * @author mowen
 *
 */
public class AlipayUtil {
	/**
	 * 發起支付
	 *
	 * @param amount          金額
	 * @param subject         主題
	 * @param notice          內容
	 * @param passback_params 自定義回調參數
	 * @return
	 * @throws AlipayApiException
	 * @throws Exception
	 */
	public static String sponsorPay(String amount, String subject, String notice, String passback_params) throws AlipayApiException {
		// 獲得初始化的AlipayClient
		DefaultAlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

		// 設置請求參數
		AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
		alipayRequest.setReturnUrl(AlipayConfig.return_url);
		alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

		// 商戶訂單號,商戶網站訂單系統中唯一訂單號,必填
		SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMddHHmmssSSS");
		String out_trade_no = simpleDateFormat.format(new Date());
		// 付款金額,必填
		String total_amount = amount;

		String body = notice;

		alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\"," + "\"total_amount\":\"" + total_amount + "\"," + "\"subject\":\"" + subject + "\"," + "\"body\":\"" + body + "\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + "\"passback_params\":\"" + passback_params + "\"}");

		// 請求
		AlipayTradePagePayResponse response = alipayClient.pageExecute(alipayRequest);

		String result = "";
		if (response.isSuccess()) {

			result = response.getBody();
		}

		return result;

	}

	/**
	 * 支付寶退款接口
	 *
	 * @param outTradeNo     商戶訂單號
	 * @param tradeNo        支付寶交易號
	 * @param refundAmount   退款的金額
	 * @param refundReason   退款的原因說明
	 * @param out_request_no 標識一次退款請求,同一筆交易多次退款需要保證唯一,如需部分退款,則此參數必傳
	 * @return
	 */
	public static String aliRefund(String outTradeNo, String tradeNo, String refundAmount, String refundReason, String out_request_no) {
		// 獲得初始化的AlipayClient
		AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

		// 設置請求參數
		AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
		alipayRequest.setReturnUrl(AlipayConfig.return_url);
		alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
		try {
			alipayRequest.setBizContent("{\"out_trade_no\":\"" + outTradeNo + "\"," + "\"trade_no\":\"" + tradeNo + "\"," + "\"refund_amount\":\"" + refundAmount + "\"," + "\"refund_reason\":\"" + refundReason + "\"," + "\"out_request_no\":\"" + out_request_no + "\"}");
			// 請求
			String result;
			result = alipayClient.execute(alipayRequest).getBody();
			return result;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 支付寶的驗簽方法
	 *
	 * @param req
	 * @return
	 */
	public static boolean checkSign(HttpServletRequest req) {
		Map<String, String[]> requestMap = req.getParameterMap();
		Map<String, String> paramsMap = new HashMap<>();
		requestMap.forEach((key, values) -> {
			String strs = "";
			for (String value : values) {
				strs = strs + value;
			}
			System.out.println(("key值為" + key + "value為:" + strs));
			paramsMap.put(key, strs);
		});

		// 調用SDK驗證簽名
		try {
			return AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
		} catch (AlipayApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("*********************驗簽失敗********************");
			return false;
		}
	}

}

請求時執行步驟:

請求時執行步驟

跳轉到支付頁面

新建index.jsp和pay.jsp

index.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="http://127.0.0.1:8080/demo/toPay">
        <label>金額</label><input type="text" name="money" >
        <input type="submit" value="付款">
    </form>
</body>
</html>

pay.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>${requestScope.result}</div>
</body>
</html>

新建一個controller類

@Controller
public class index {

    @RequestMapping
    public String index() {
        return "index";
    }

    @RequestMapping(value = "/toPay",method = RequestMethod.POST)
    public String toPay(String money, ModelMap map) {
        try {
            String result= AlipayUtil.sponsorPay(money,"我買了啥?","不知道啊",null);
            map.addAttribute("result",result);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            System.out.println("請求支付寶接口失敗");
        }
        return "pay";
    }
}

項目開啟后訪問進入index.jsp

image

輸入框內輸入金額點擊付款就會進入后台控制器的toPay方法,然后請求支付寶接口,把請求得到的body內容放到request作用域,

轉跳到pay.jsp,這個頁面其實就是把得到的內容放到一個div里,然后會自動跳轉到付款頁面,因為內容就是一個會自動提交的from表單,restful模式的話,把它傳給前端,前端找個div放進去就會自動跳轉。

image

使用沙箱版的支付寶就可付款,上線環境一樣的道理。

同步通知

官方文檔:https://docs.open.alipay.com/59/103665

支付寶對商戶的請求數據處理完成后,會將處理的結果數據直接通知給商戶。這些處理結果數據就是同步通知參數。比如我填:

#頁面跳轉同步通知頁面路徑
alipay.return_url=http://127.0.0.1:8080/demo/toReturnURL

后台控制器添加方法:

    @RequestMapping(value = "/toReturnURL")
    public String toReturnURL() {
        return "returnURL";
    }

添加returnURL.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <span>同步頁面</span>
</body>
</html>


到達付款頁面付款幾秒后會根據你填的同步通知地址請求,我后端又跳到jsp頁面,也可以直接寫一個htnl頁面的路徑地址,參數有以下這些,也可以用參數驗證完后再跳轉頁面


參數 參數名稱 類型 描述 范例
out_trade_no 商戶訂單號 String(64) 原支付請求的商戶訂單號 6823789339978248
total_amount
訂單金額 Number(9,2) 本次交易支付的訂單金額,單位為人民幣(元),精確到小數點后2位 20.00
sign 簽名 String(256) 601510b7970e52cc63db0f44997cf70e
sign_type 簽名類型 String(10)

簽名算法類型,目前支持RSA2和RSA,推薦使用RSA2

RSA2
trade_no 支付寶交易號 String(64)

支付寶交易憑證號

2013112011001004330000121536
auth_app_id 授權方的app_id String(32) 授權方的appid,由於本接口暫不開放第三方應用授權,因此auth_app_id=app_id 2014072300007148
app_id 開發者的app_id String(32) 支付寶分配給開發者的應用 ID 2014072300007148
seller_id 賣家支付寶用戶號 String(30) 賣家支付寶用戶號 2088101106499364

timestamp


異步通知

當收銀台調用預下單請求 API 生成二維碼展示給用戶后,用戶通過手機掃描二維碼進行支付,支付寶會將該筆訂單的變更信息,沿着商戶調用預下單請求時所傳入的異步通知地址 notify_url,通過 POST 請求的形式將支付結果作為參數通知到商戶系統。

異步回調地址狀態碼(http狀態碼) 為 200 時表示異步通知成功,返回碼為 404 或 500 時則表示服務器內部錯誤,需要商家自行排查。

商家如果因為其他原因沒有收到支付寶服務端返回的異步通知,開發者可以在開發者社區查看自查自查方案:

官方文檔:https://docs.open.alipay.com/194/103296


異步通知和同步一樣的地方在於,付款后帶一些參數返回到你服務器,讓你可以進行一下驗證,

異步通知只有掃碼才會有。

異步通知最重要的是要外網能訪問到你。

所以連接路由器的話,需要用內網穿透,讓外網能訪問到你;

我使用的是http://www.ngrok.cc/

測試的加開個免費的通道即可(關於開啟通道失敗問題,我家是移動寬帶,免費的服務器連接不了,出現連接失敗可以用手機開個網試試,不然直接買vip的)

image

開啟通道后:官網教程http://www.ngrok.cc/_book/

image

異步通知地址就可填寫:

#服務器異步通知頁面路徑
alipay.notify_url=http://paydemo.free.idcfengye.com/demo/notifyURL

后台控制器添加方法:

    @RequestMapping(value = "/notifyURL",method = RequestMethod.POST)
    public void notifyURL(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException {

        // 獲取支付寶POST過來反饋信息(官方案例)
        Map<String, String> params = new HashMap<String, String>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> 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);
        }

        // 調用SDK驗證簽名
        boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);

        if (signVerified) {// 驗證成功

            // 交易狀態
            String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

            if (trade_status.equals("TRADE_FINISHED")) {
                System.out.println("交易結束,不可退款");
            } else if (trade_status.equals("TRADE_SUCCESS")) {
                System.out.println("交易支付成功");
            }

        } else {// 驗證失敗

        }
    }

用戶付款后會打印:

image

params集合里有着這次訂單的所有資料,具體有哪些參數請看官方文檔。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM