支付介面呼叫(支付寶、微信)
一、支付寶支付
1、官方文件是最好的教程:
①電腦網站支付文件:https://docs.open.alipay.com/270/105899/
②支付寶沙箱使用教程:https://docs.open.alipay.com/200/105311/
③呼叫支付寶相關介面的應用建立:https://open.alipay.com/developmentAccess/developmentAccess.htm
2、支付寶支付示例:
在使用支付寶支付時,先要在螞蟻金服開放平臺中下載alipay-sdk-java-3.4.49.ALL.jar。
在呼叫支付介面乃至很多第三方介面時,很多都有一個發起呼叫的方法和一個呼叫成功後的回撥方法。在支付寶支付中也是一樣,先看看發起支付的方法:
/** * 發起支付 * @param httpRequest * @param httpResponse * @param out_trade_no 訂單號 * @param total_amount 訂單金額 * @param subject 訂單名稱 * @param body 商品描述 * @throws ServletException * @throws IOException */ @RequestMapping("aliPay") public void aliPay(HttpServletRequest httpRequest, HttpServletResponse httpResponse,String out_trade_no,String total_amount, String subject,String body) throws ServletException, IOException{ doPost(httpRequest, httpResponse, out_trade_no, total_amount, subject, body); } /** * @param httpRequest * @param httpResponse * @param out_trade_no 訂單號 * @param total_amount 訂單金額 * @param subject 訂單名稱 * @param body 商品描述 * @throws ServletException * @throws IOException */ public void doPost(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String out_trade_no,String total_amount, String subject,String body) throws ServletException, IOException { AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.GATEWAYURL, AlipayConfig.APP_ID, AlipayConfig.APP_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGN_TYPE); // 獲得初始化的AlipayClient // 建立API對應的request AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(AlipayConfig.RETURN_URL); alipayRequest.setNotifyUrl(AlipayConfig.NOTIFY_URL); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"total_amount\":\""+ total_amount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); String form = ""; try { // 呼叫SDK生成表單 form = alipayClient.pageExecute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } httpResponse.setContentType("text/html;charset=" + AlipayConfig.CHARSET); // 直接將完整的表單html輸出到頁面 httpResponse.getWriter().write(form); httpResponse.getWriter().flush(); httpResponse.getWriter().close(); }
支付寶支付成功後的回撥方法:
/** * 支付寶支付回撥 * @param request * @return * @throws UnsupportedEncodingException * @throws AlipayApiException */ @RequestMapping("aliPaySuccess") public String aliPaySuccess(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException { System.out.println("------------------------------------>回撥成功!"); 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); } boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); //呼叫SDK驗證簽名 //——請在這裡編寫您的程式(以下程式碼僅作參考)—— /* 實際驗證過程建議商戶務必新增以下校驗: 1、需要驗證該通知資料中的out_trade_no是否為商戶系統中建立的訂單號, 2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單建立時的金額), 3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email) 4、驗證app_id是否為該商戶本身。 */ if(signVerified) { //驗證成功 //商戶訂單號 String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); //支付寶交易號 String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); System.out.println("trade_no"+out_trade_no); System.out.println("trade_no"+trade_no); System.out.println("支付成功"); }else { //驗證失敗 System.out.println("支付失敗"); } return "testPay/aliPayPage"; }
AlipayConfig.java常量類:
package com.mfc.util;
/**
* @author 74790
* 支付寶支付相關引數
*/
public class AlipayConfig {
// 支付寶閘道器
public static String GATEWAYURL = "https://openapi.alipaydev.com/gateway.do";
// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
public static final String APP_ID = "";
// 商戶私鑰,您的PKCS8格式RSA2私鑰
public static final String APP_PRIVATE_KEY = "";
public static final String FORMAT = "json";
// 字元編碼格式
public static final String CHARSET = "utf-8";
// 支付寶公鑰,檢視地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
public static final String ALIPAY_PUBLIC_KEY = "";
// 簽名方式
public static final String SIGN_TYPE = "RSA2";
//頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義引數,必須外網可以正常訪問
public static final String RETURN_URL="";
// 伺服器非同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義引數,必須外網可以正常訪問
public static final String NOTIFY_URL="";
}
3、支付寶支付訂單實現思路:
①.點選支付呼叫TestPayCtrl中的aliPay方法發起支付,此時會跳往支付寶支付介面
②.在發起支付的aliPay方法中(本示例中其實是在doPost方法中),呼叫service方法將支付相關資訊(如訂單號、訂單金額、商品資訊等)存進自己系統的業務表中(這裡只演示支付過程,所以沒有將支付相關資訊存進自己系統的業務表中)。
③.支付寶支付成功後會回撥本系統的一個方法,我這裡回撥的是TestPayCtrl中的aliPaySuccess方法,支付寶會反饋給回撥方法一些引數: 如out_trade_no(商戶的訂單號)、trade_no(支付寶交易號)等。<br/>
④.在回撥方法中就可以通過out_trade_no(商戶的訂單號)取支付相關資訊(支付相關資訊在步驟2中已經存進了自己的業務表)。取出這些支付相關資訊就可以寫自己網站的邏輯了。<br/>
二、微信支付
1、同樣官方文件是最好的教程:
官方支付相關文件:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_2
微信支付(掃碼支付)教程:https://www.cnblogs.com/gdufs/p/7230950.html
2、微信支付示例:
這裡使用Maven依賴:
<!-- 微信支付 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<!-- google二維碼工具 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.1.0</version>
</dependency>
微信支付發起支付:
private WxPayConfig config = new WxPayConfig();
private WXPay wxPay = new WXPay(config);
@RequestMapping("wxpay/pay")
public void pay(HttpServletResponse response){
//這裡執行商戶系統建立新的訂單操作,寫商戶自己的邏輯
//設定微信支付請求引數
Map<String, String> data = new HashMap<String, String>();
//商品簡單描述,該欄位請按照規範傳遞,具體請見引數規定,必填
data.put("body", "微信支付測試");
//商戶訂單號 ,必填
data.put("out_trade_no", "20190106");
//裝置號,非必填
//data.put("device_info", "");
//標價幣種,預設人民幣(CNY),非必填
data.put("fee_type", "CNY");
//標價金額,必填
data.put("total_fee", "1");
//支援IPV4和IPV6兩種格式的IP地址。呼叫微信支付API的機器IP,必填
data.put("spbill_create_ip", "192.168.0.11");
//非同步接收微信支付結果通知的回撥地址,通知url必須為外網可訪問的url,不能攜帶引數。
data.put("notify_url", "");
//交易型別,必填
data.put("trade_type", "NATIVE"); // 此處指定為掃碼支付
//商品ID,非必填
data.put("product_id", "12");
try {
//發起支付
Map<String, String> resp = wxPay.unifiedOrder(data);
//獲取二維碼URL
String code_url = resp.get("code_url");
//根據URL生成二維碼
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
//設定二維碼引數
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix bitMatrix = multiFormatWriter.encode(code_url, BarcodeFormat.QR_CODE, 300, 300, hints);
//返回二維碼
MatrixToImageWriter.writeToStream(bitMatrix, "jpg", response.getOutputStream());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
支付成功回撥:
/**
* 支付結果回撥
* @return
* @throws Exception
*/
@RequestMapping("/wxpay/notify_url")
public void notifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 讀取回調內容
InputStream inputStream;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
// 支付結果通知的xml格式資料
String notifyData = sb.toString();
// 轉換成map
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
//支付確認內容
String resXml = "";
//驗證簽名
if (wxPay.isPayResultNotifySignatureValid(notifyMap)) { // 簽名正確
System.out.println("返回的商戶訂單號:"+notifyMap.get("out_trade_no"));
System.out.println("這裡需要根據商戶的訂單號在自己系統中查詢,判斷這個訂單號是不是本系統的訂單號");
if(notifyMap.get("out_trade_no") != null) {
if("SUCCESS".equals(notifyMap.get("result_code"))) { //交易成功
// TODO:更新訂單
System.out.println("訂單" + notifyMap.get("out_trade_no") + "微信支付成功");
} else { //交易失敗
System.out.println("訂單" + notifyMap.get("out_trade_no") + "微信支付失敗");
}
}
// 注意特殊情況:訂單已經退款,但收到了支付結果成功的通知,不應把商戶側訂單狀態從退款改成支付成功
//設定成功確認內容
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
}
else { // 簽名錯誤,如果資料裡沒有sign欄位,也認為是簽名錯誤
//設定失敗確認內容
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg></return_msg>" + "</xml> ";
System.out.println("訂單" + notifyMap.get("out_trade_no") + "微信支付失敗");
}
//傳送通知
response.getWriter().println(resXml);
}
實現的微信支付相關配置的引數類:
package com.mfc.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import com.github.wxpay.sdk.WXPayConfig;
public class WxPayConfig implements WXPayConfig{
private byte[] certData;
//初始化退款、撤銷時的商戶證書
public WxPayConfig() {
try {
String certPath = "D://第三方開放平臺/wx_apiclient_cert.p12";
File file = new File(certPath);
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int) file.length()];
certStream.read(this.certData);
certStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 微信支付分配的公眾賬號ID(企業號corpid即為此appId)
* */
public String getAppID() {
return "";
}
/**
* 微信支付分配的商戶號
* */
public String getMchID() {
return "";
}
public String getKey() {
return "";
}
public int getHttpConnectTimeoutMs() {
return 8000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
public InputStream getCertStream() {
ByteArrayInputStream certBis;
certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
}
3、微信支付實現思路:
①.點選支付呼叫TestPayCtrl中的pay方法發起支付,此時彈出微信支付二維碼
②.在發起支付的pay方法中,呼叫service方法將支付相關資訊(如訂單號、訂單金額、商品資訊等)存進自己系統的業務表中。
③.微信支付成功後會回撥本系統的一個方法,我這裡回撥的是TestPayCtrl中的notifyUrl方法,微信會反饋給回撥方法一些引數:如out_trade_no(商戶的訂單號)等。
④.在回撥方法中就可以通過out_trade_no(商戶的訂單號)取支付相關資訊(支付相關資訊在步驟2中已經存進了自己的業務表)。取出這些支付相關資訊就可以寫自己網站的邏輯了。