paypal支付標準按鈕(from表單)整合
1.簡介
PayPal是倍受全球億萬使用者追捧的國際貿易支付工具,即時支付,即時到賬,全中文操作介面,能通過中國的本地銀行輕鬆提現,解決外貿收款難題,助您成功開展海外業務,決勝全球。註冊PayPal後就可立即開始接受信用卡付款。作為線上付款服務商,PayPal是您向全世界近2.54億的使用者敞開大門的最快捷的方式。最大的好處是,註冊完全免費!集國際流行的信用卡,借記卡,電子支票等支付方式於一身。幫助買賣雙方解決各種交易過程中的支付難題。PayPal是名副其實的全球化支付平臺,服務範圍超過200個市場,支援的幣種超過100個。在跨國交易中,將近70%的線上跨境買家更喜歡用PayPal支付海外購物款項。
PayPal提供了多種支付方式:
- 標準支付
- 快速支付
- 其中標準支付譽為最佳實踐。
注意:paypal支付國內賬號不能付款給國內賬號
2.PayPal的相關URL
沙箱(開發者)網址:https://developer.paypal.com/
沙箱(測試使用者)登入地址:https://www.sandbox.paypal.com/
技術支援地址(paypal開發人員):https://www.paypal-support.com/s/account-overview
官方文件:
- 付款按鈕資料:https://developer.paypal.com/docs/integration/web/
- IPN(支付回撥)資料:https://developer.paypal.com/docs/api-basics/notifications/ipn/ht-ipn/
- PDT(資料傳輸)資料:https://developer.paypal.com/docs/api-basics/notifications/payment-data-transfer/#get-started
3.整合步驟
1、在paypal頁面建立Buttons(PayPal付款按鈕)並複製程式碼到專案
2、使用者點選支付按鈕
3、進入PayPal支付頁面
4、使用者登入後確認支付
5、顯示支付成功資訊
4.整合步驟實現
(1)註冊賬號
在PayPal正式網站https://www.paypal.com中註冊一個賬號,如果公司沒有給你相關資訊的話,先註冊一個個人賬號也是一樣的。
(2)進入開發者介面建立相關資訊
1、在開發者平臺https://developer.paypal.com/,登入剛建立的賬號
2、登入成功後,選擇:SANBOX下的Accounts標籤
3、建立個人賬號和商家賬號用於測試沙箱環境
(3)建立按鈕
1、在PayPal沙箱測試賬號網站https://www.sandbox.paypal.com/登入剛剛建立的沙箱商家賬號
2、建立支付按鈕
(4)PDT令牌獲取
(5)IPN回撥亂碼處理
(6)示例程式碼
控制器程式碼如下:
@Controller @RequestMapping("/paypalC") public class PaypalC { @Autowired private PaypalS paypalS; //TODO 標準付款按鈕整合 /**交易取消 */ @RequestMapping(method = RequestMethod.GET, value = "cancel") public String cancel(){ return "b2/paypal/cancel"; } /**獲取標準付款頁面 */ @RequestMapping(method = RequestMethod.GET, value = "standardPayment", produces="text/html;charset=UTF-8") public String standardPayment(){ return "b2/paypal/standardPayment"; } /**PDT立即回撥(由使用者在paypal點選返回商家網站時呼叫,不點選則不會呼叫,含跳轉路徑) */ @RequestMapping(value = "pdtWebhooks", produces="text/html;charset=UTF-8") public String pdtWebhooks(HttpServletRequest req, HttpServletResponse resp){ return paypalS.pdtWebhooks(req, resp); } /**IPN回撥(由paypal支付成功後呼叫,最多重複呼叫15次) */ @ResponseBody @RequestMapping(method = RequestMethod.POST, value = "ipnWebhooks", produces="text/html;charset=UTF-8") public String ipnWebhooks(HttpServletRequest req, HttpServletResponse resp) { return paypalS.ipnWebhooks(req, resp); } }View Code
業務層程式碼如下:
import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.springframework.stereotype.Service; import com.grt.b2.method.PaypalUtils; import com.grt.v3.method.OUtils; /**Paypal支付service類 */ @Service public class PaypalS { //TODO 標準付款按鈕整合 /**PDT立即回撥(由使用者在paypal點選返回商家網站時呼叫,不點選則不會呼叫,含跳轉路徑) */ public String pdtWebhooks(HttpServletRequest req, HttpServletResponse resp) { String tx = req.getParameter("txn_id");//訂單id String stringRes = PaypalUtils.getPDTWebHooks(tx); if(!OUtils.isEmpty(stringRes)){ String[] res = stringRes.split("\\r?\\n"); if("SUCCESS".equals(res[0])){ System.out.println("驗證PDT資料成功,進行訂單入庫"); Map<String, String> data = new HashMap<String, String>(); for (String str : res) { if(str.indexOf("=") > 0){ String[] s = str.split("="); data.put(s[0], s.length > 1 ? s[1] : ""); } } return "b2/paypal/success"; } } return "b2/paypal/failure"; } /**IPN回撥(由paypal支付成功後呼叫,最多重複呼叫15次) */ @SuppressWarnings("unchecked") public String ipnWebhooks(HttpServletRequest req, HttpServletResponse resp) { //1.獲取ipn傳遞的資料 Map<String, String> data = new HashMap<String, String>(); List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("cmd", "_notify-validate")); Enumeration<String> e = req.getParameterNames(); while(e.hasMoreElements()){ String name = e.nextElement(); String value = req.getParameter(name); params.add(new BasicNameValuePair(name, value)); data.put(name, value); } //2.把ipn傳遞資料傳送給paypal並獲取返回資料進行驗證 String res = PaypalUtils.getIPNWebHooks(params); System.out.println("IPN資料\n"+res); if ("VERIFIED".equals(res)) {//驗證IPN回撥資料成功 System.out.println("驗證IPN回撥資料成功,進行訂單入庫"); }else if("INVALID".equals(res)) {//驗證IPN回撥資料失敗 System.out.println("驗證IPN回撥資料失敗,進行日誌記錄"); }else{//驗證IPN回撥資料異常 System.out.println("驗證IPN回撥資料異常,進行日誌記錄"); } return ""; } }View Code
PayPal工具類如下:
import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.Consts; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import com.grt.michael.method.tigo.HttpsUtils; import com.grt.v3.method.OUtils; /**Paypal支付工具類 */ public class PaypalUtils { //TODO 標準付款按鈕整合 private static final String SANDBOX_URL = "https://www.sandbox.paypal.com/cgi-bin/webscr"; private static final String LIVE_URL = "https://www.paypal.com/cgi-bin/webscr"; private static final String pdt_key = "";//PDT金鑰,需要自己去paypal拿 /**獲取PDT驗證資料 */ public static String getPDTWebHooks(String tx){ Map<String, String> headers = new HashMap<String, String>(); List<NameValuePair> params = new ArrayList<NameValuePair>(); headers.put("Content-Type", "application/x-www-form-urlencoded"); params.add(new BasicNameValuePair("cmd", "_notify-synch")); params.add(new BasicNameValuePair("tx", tx)); params.add(new BasicNameValuePair("at", pdt_key)); String result = sendPost(SANDBOX_URL, headers, params); if(!OUtils.isEmpty(result)) return URLDecoderString(result); return result; } /**獲取IPN驗證資料 */ public static String getIPNWebHooks(List<NameValuePair> params){ Map<String, String> headers = new HashMap<String, String>(); headers.put("Content-Type", "application/x-www-form-urlencoded"); return sendPost(SANDBOX_URL, headers, params); } /**URL編碼解碼 */ public static String URLDecoderString(String str) { String result = ""; try { result = java.net.URLDecoder.decode(str, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } /**post請求方法 */ private static String sendPost(String url, Map<String, String> headers, List<NameValuePair> params) { String resultData = null; try{ CloseableHttpClient http = HttpsUtils.getHttpClient(); HttpPost httpost = new HttpPost(url); //設定header if (headers != null && headers.size() > 0) { for (Map.Entry<String, String> entry : headers.entrySet()) { httpost.setHeader(entry.getKey(), entry.getValue()); } } //組織請求引數 if (params != null && params.size() > 0) { httpost.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8)); } // 執行結果 for (int i = 0; i < 3; i++) { try { HttpResponse res = http.execute(httpost); if (res.getStatusLine().getStatusCode() != 200) continue; resultData = EntityUtils.toString(res.getEntity()); return resultData; } catch (IOException e) { e.printStackTrace(); continue; } finally { httpost.releaseConnection(); } } }catch (Exception e) { e.printStackTrace(); } return resultData; } }View Code
HttpUtils工具類(httpclient-4.5.jar、httpcore-4.4.1.jar)如下:
import org.apache.commons.collections.MapUtils; import org.apache.http.*; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; public class HttpsUtils { private static final String HTTP = "http"; private static final String HTTPS = "https"; private static SSLConnectionSocketFactory sslsf = null; private static PoolingHttpClientConnectionManager cm = null; private static SSLContextBuilder builder = null; static { try { builder = new SSLContextBuilder(); // 全部信任 不做身份鑑定 builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return true; } }); sslsf = new SSLConnectionSocketFactory(builder.build(), new String[] { "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2" }, null, NoopHostnameVerifier.INSTANCE); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create().register(HTTP, new PlainConnectionSocketFactory()).register(HTTPS, sslsf).build(); cm = new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(200);// max connection } catch (Exception e) { e.printStackTrace(); } } /** * httpClient post請求 * * @param url * 請求url * @param header * 頭部資訊 * @param param * 請求引數 form提交適用 * @param entity * 請求實體 json/xml提交適用 * @return 可能為空 需要處理 * @throws Exception * */ public static String doGet(String url) throws Exception { String result = ""; CloseableHttpClient httpClient = null; try { httpClient = getHttpClient(); HttpGet httpGet = new HttpGet(url); RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();// 設定請求和傳輸超時時間 httpGet.setConfig(requestConfig); HttpResponse httpResponse = httpClient.execute(httpGet); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { HttpEntity resEntity = httpResponse.getEntity(); result = EntityUtils.toString(resEntity); } else { readHttpResponse(httpResponse); } } catch (Exception e) { throw e; } finally { if (httpClient != null) { httpClient.close(); } } return result; } /** * httpClient post請求 * * @param url * 請求url * @param header * 頭部資訊 * @param param * 請求引數 form提交適用 * @param entity * 請求實體 json/xml提交適用 * @return 可能為空 需要處理 * @throws Exception * */ public static String doPost(String url, Map<String, String> header, Map<String, String> param, HttpEntity entity) throws Exception { String result = ""; CloseableHttpClient httpClient = null; try { httpClient = getHttpClient(); HttpPost httpPost = new HttpPost(url); // 設定頭資訊 if (MapUtils.isNotEmpty(header)) { for (Map.Entry<String, String> entry : header.entrySet()) { httpPost.addHeader(entry.getKey(), entry.getValue()); } } // 設定請求引數 if (MapUtils.isNotEmpty(param)) { List<NameValuePair> formparams = new ArrayList<NameValuePair>(); for (Map.Entry<String, String> entry : param.entrySet()) { // 給引數賦值 formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(formparams, Consts.UTF_8); httpPost.setEntity(urlEncodedFormEntity); } // 設定實體 優先順序高 if (entity != null) { httpPost.setEntity(entity); } HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse = httpClient.execute(httpGet); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { HttpEntity resEntity = httpResponse.getEntity(); result = EntityUtils.toString(resEntity); } else { readHttpResponse(httpResponse); } } catch (Exception e) { throw e; } finally { if (httpClient != null) { httpClient.close(); } } return result; } public static CloseableHttpClient getHttpClient() throws Exception { CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(cm).setConnectionManagerShared(true).build(); return httpClient; } public static String readHttpResponse(HttpResponse httpResponse) throws ParseException, IOException { StringBuilder builder = new StringBuilder(); // 獲取響應訊息實體 HttpEntity entity = httpResponse.getEntity(); // 響應狀態 builder.append("status:" + httpResponse.getStatusLine()); builder.append("headers:"); HeaderIterator iterator = httpResponse.headerIterator(); while (iterator.hasNext()) { builder.append("\t" + iterator.next()); } // 判斷響應實體是否為空 if (entity != null) { String responseString = EntityUtils.toString(entity); builder.append("response length:" + responseString.length()); builder.append("response content:" + responseString.replace("\r\n", "")); } return builder.toString(); } }View Code
paypal支付按鈕(standardPayment.html)程式碼如下:
<html> <head> <meta charset="UTF-8" /> <title>Insert title here</title> </head> <body> <form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post" target="_top"> <input type="hidden" name="cmd" value="_xclick"><%--支付型別--%> <input type="hidden" name="business" value="[email protected]"><%--商家賬號--%> <input type="hidden" name="hosted_button_id" value="CWA2QSSG7CDBA"><%--按鈕id,由paypal生成--%> <input TYPE="hidden" name="charset" value="utf-8"><%--編碼--%> <input type="hidden" name="invoice" value="ABC123"><%--自定義訂單編號--%> <input type="hidden" name="item_name" value="Server服務開通"><%--商品名稱--%> <input type="hidden" name="item_number" value="1"><%--商品數量--%> <input type="hidden" name="amount" value="5.95"><%--商品價格--%> <input type="hidden" name="currency_code" value="USD"><%--貨幣--%> <input type="hidden" name="return" value=""><%--支付成功(PDT地址)--%> <input type="hidden" name="cancel_return" value=""><%--支付失敗頁面--%> <%-- <input type="hidden" name="notify_url" value="">回撥路徑(IPN地址) --%> <input type='hidden' name="no_shipping" value="1"><%--不要求客戶提供收貨地址--%> <input type="hidden" name="rm" value="2"><%--return有地址的話,必須指定這個引數--%> <input type="hidden" name="no_note" value="老子不想付款啊。。。。"><%--付款說明--%> <input type="hidden" name="image_url" value="https://www.paypal.com/zh_XC/i/btn/btn_buynow_SM.gif"><%--圖片--%> <input type="hidden" name="custom" value="admin"><%--自定義變數傳遞伺服器需要資訊--%> <input type="image" src="https://www.sandbox.paypal.com/zh_XC/i/btn/btn_buynow_SM.gif" border="0" name="submit" alt="PayPal——最安全便捷的線上支付方式!"> <img alt="" border="0" src="https://www.sandbox.paypal.com/zh_XC/i/scr/pixel.gif" width="1" height="1"> </form> </body> </html>View Code
cancel.html程式碼如下:
<html> <head> <meta charset="UTF-8" /> <title>Insert title here</title> </head> <body> <h1>使用者取消支付...</h1> </body> </html>View Code
success.html程式碼如下:
<html> <head> <meta charset="UTF-8" /> <title>Insert title here</title> </head> <body> <h1>使用者支付成功</h1> </body> </html>View Code
failure.html程式碼如下:
<html> <head> <meta charset="UTF-8" /> <title>Insert title here</title> </head> <body> <h1>使用者支付失敗</h1> </body> </html>View Code