1,開通海關報關產品 ,不能登錄賬號,登陸賬號是看不到產品的
地址:https://b.alipay.com/order/productDetail.htm?productId=2015112418944074
2,產品開通成功後,查看報關的開發文檔
地址:https://docs.open.alipay.com/155/104778/
3,官方提供了項目demo,支持三種語言
4,整合到springboot 項目,使用MD5加密方式
(1),application.yml裏面添加,根據自己的參數修改裏面值
alipay:
#合作身份者ID
partnerId: 2088***********
#商戶的私鑰 根據加密類型,填寫:MD5 ,RSA
privateKey: ***********
#字符編碼格式
inputCharset: utf-8
#簽名方式
ignType: MD5
#廣州總署海關
customs: ZONGSHU
#商戶海關備案號
mchCustomsNo: ***********
#商戶海關備案名稱
mchCustomsName: ***********
#接口名稱
service: alipay.acquire.customs
#接口地址
payGateWay: https://mapi.alipay.com/gateway.do?
(2)下面代碼是工具類,httpclient的一些代碼
package io.renren.utils.alipay;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AlipayUtil {
/**
* 生成請求url
* @param params
* @param key
* @param paygateway
* @param input_charset
* @return
*/
public static String createUrl( Map<String,String> params ,String key, String paygateway,String input_charset,String sign_type) {
String prestr = "";
prestr = prestr + key;
String sign = DigestUtils.md5Hex(getContent(params, key));
String parameter = "";
parameter = parameter + paygateway;
List<String> keys = new ArrayList<>(params.keySet());
for (int i = 0; i < keys.size(); i++) {
try {
parameter = parameter + keys.get(i) + "="
+ URLEncoder.encode(params.get(keys.get(i)), input_charset) + "&";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
parameter = parameter + "sign=" + sign + "&sign_type="+sign_type;
return parameter;
}
/**
* 拼接請求參數
* @param params
* @param privateKey
* @return
*/
private static String getContent(Map params, String privateKey) {
Map map = params;
List keys = new ArrayList(map.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = (String) keys.get(i);
String value = (String) map.get(key);
if (i == keys.size() - 1) {
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
String p = "";
if(StringUtils.isNotBlank(privateKey)){
p = prestr+privateKey;
} else {
p = prestr;
}
return p;
}
}
package io.renren.utils.alipay.httpClient;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.*;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;
import java.io.File;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
public class HttpProtocolHandler {
private static String DEFAULT_CHARSET = "GBK";
/** 連接超時時間,由bean factory設置,缺省爲8秒鐘 */
private int defaultConnectionTimeout = 8000;
/** 迴應超時時間, 由bean factory設置,缺省爲30秒鐘 */
private int defaultSoTimeout = 30000;
/** 閒置連接超時時間, 由bean factory設置,缺省爲60秒鐘 */
private int defaultIdleConnTimeout = 60000;
private int defaultMaxConnPerHost = 30;
private int defaultMaxTotalConn = 80;
/** 默認等待HttpConnectionManager返回連接超時(只有在達到最大連接數時起作用):1秒*/
private static final long defaultHttpConnectionManagerTimeout = 3 * 1000;
/**
* HTTP連接管理器,該連接管理器必須是線程安全的.
*/
private HttpConnectionManager connectionManager;
private static HttpProtocolHandler httpProtocolHandler = new HttpProtocolHandler();
/**
* 工廠方法
*
* @return
*/
public static HttpProtocolHandler getInstance() {
return httpProtocolHandler;
}
/**
* 私有的構造方法
*/
private HttpProtocolHandler() {
// 創建一個線程安全的HTTP連接池
connectionManager = new MultiThreadedHttpConnectionManager();
connectionManager.getParams().setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);
connectionManager.getParams().setMaxTotalConnections(defaultMaxTotalConn);
IdleConnectionTimeoutThread ict = new IdleConnectionTimeoutThread();
ict.addConnectionManager(connectionManager);
ict.setConnectionTimeout(defaultIdleConnTimeout);
ict.start();
}
/**
* 執行Http請求
*
* @param request 請求數據
* @param strParaFileName 文件類型的參數名
* @param strFilePath 文件路徑
* @return
* @throws HttpException, IOException
*/
public HttpResponse execute(HttpRequest request, String strParaFileName, String strFilePath) throws HttpException, IOException {
HttpClient httpclient = new HttpClient(connectionManager);
// 設置連接超時
int connectionTimeout = defaultConnectionTimeout;
if (request.getConnectionTimeout() > 0) {
connectionTimeout = request.getConnectionTimeout();
}
httpclient.getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
// 設置迴應超時
int soTimeout = defaultSoTimeout;
if (request.getTimeout() > 0) {
soTimeout = request.getTimeout();
}
httpclient.getHttpConnectionManager().getParams().setSoTimeout(soTimeout);
// 設置等待ConnectionManager釋放connection的時間
httpclient.getParams().setConnectionManagerTimeout(defaultHttpConnectionManagerTimeout);
String charset = request.getCharset();
charset = charset == null ? DEFAULT_CHARSET : charset;
HttpMethod method = null;
//get模式且不帶上傳文件
if (request.getMethod().equals(HttpRequest.METHOD_GET)) {
method = new GetMethod(request.getUrl());
method.getParams().setCredentialCharset(charset);
// parseNotifyConfig會保證使用GET方法時,request一定使用QueryString
method.setQueryString(request.getQueryString());
} else if(strParaFileName.equals("") && strFilePath.equals("")) {
//post模式且不帶上傳文件
method = new PostMethod(request.getUrl());
((PostMethod) method).addParameters(request.getParameters());
method.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; text/html; charset=" + charset);
}
else {
//post模式且帶上傳文件
method = new PostMethod(request.getUrl());
List<Part> parts = new ArrayList<Part>();
for (int i = 0; i < request.getParameters().length; i++) {
parts.add(new StringPart(request.getParameters()[i].getName(), request.getParameters()[i].getValue(), charset));
}
//增加文件參數,strParaFileName是參數名,使用本地文件
parts.add(new FilePart(strParaFileName, new FilePartSource(new File(strFilePath))));
// 設置請求體
((PostMethod) method).setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[0]), new HttpMethodParams()));
}
// 設置Http Header中的User-Agent屬性
method.addRequestHeader("User-Agent", "Mozilla/4.0");
HttpResponse response = new HttpResponse();
try {
httpclient.executeMethod(method);
if (request.getResultType().equals(HttpResultType.STRING)) {
response.setStringResult(method.getResponseBodyAsString());
} else if (request.getResultType().equals(HttpResultType.BYTES)) {
response.setByteResult(method.getResponseBody());
}
response.setResponseHeaders(method.getResponseHeaders());
} catch (UnknownHostException ex) {
return null;
} catch (IOException ex) {
return null;
} catch (Exception ex) {
return null;
} finally {
method.releaseConnection();
}
return response;
}
/**
* 將NameValuePairs數組轉變爲字符串
*
* @param nameValues
* @return
*/
protected String toString(NameValuePair[] nameValues) {
if (nameValues == null || nameValues.length == 0) {
return "null";
}
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < nameValues.length; i++) {
NameValuePair nameValue = nameValues[i];
if (i == 0) {
buffer.append(nameValue.getName() + "=" + nameValue.getValue());
} else {
buffer.append("&" + nameValue.getName() + "=" + nameValue.getValue());
}
}
return buffer.toString();
}
}
package io.renren.utils.alipay.httpClient;
import org.apache.commons.httpclient.NameValuePair;
/* *
*類名:HttpRequest
*功能:Http請求對象的封裝
*詳細:封裝Http請求
*版本:3.3
*日期:2011-08-17
*說明:
*以下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
*/
public class HttpRequest {
/** HTTP GET method */
public static final String METHOD_GET = "GET";
/** HTTP POST method */
public static final String METHOD_POST = "POST";
/**
* 待請求的url
*/
private String url = null;
/**
* 默認的請求方式
*/
private String method = METHOD_POST;
private int timeout = 0;
private int connectionTimeout = 0;
/**
* Post方式請求時組裝好的參數值對
*/
private NameValuePair[] parameters = null;
/**
* Get方式請求時對應的參數
*/
private String queryString = null;
/**
* 默認的請求編碼方式
*/
private String charset = "GBK";
/**
* 請求發起方的ip地址
*/
private String clientIp;
/**
* 請求返回的方式
*/
private HttpResultType resultType = HttpResultType.BYTES;
public HttpRequest(HttpResultType resultType) {
super();
this.resultType = resultType;
}
/**
* @return Returns the clientIp.
*/
public String getClientIp() {
return clientIp;
}
/**
* @param clientIp The clientIp to set.
*/
public void setClientIp(String clientIp) {
this.clientIp = clientIp;
}
public NameValuePair[] getParameters() {
return parameters;
}
public void setParameters(NameValuePair[] parameters) {
this.parameters = parameters;
}
public String getQueryString() {
return queryString;
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* @return Returns the charset.
*/
public String getCharset() {
return charset;
}
/**
* @param charset The charset to set.
*/
public void setCharset(String charset) {
this.charset = charset;
}
public HttpResultType getResultType() {
return resultType;
}
public void setResultType(HttpResultType resultType) {
this.resultType = resultType;
}
}
package io.renren.utils.alipay.httpClient;
import io.renren.config.AlipayConfig;
import org.apache.commons.httpclient.Header;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.UnsupportedEncodingException;
/* *
*類名:HttpResponse
*功能:Http返回對象的封裝
*詳細:封裝Http返回信息
*版本:3.3
*日期:2011-08-17
*說明:
*以下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
*/
public class HttpResponse {
@Autowired
private AlipayConfig alipayConfig;
/**
* 返回中的Header信息
*/
private Header[] responseHeaders;
/**
* String類型的result
*/
private String stringResult;
/**
* btye類型的result
*/
private byte[] byteResult;
public Header[] getResponseHeaders() {
return responseHeaders;
}
public void setResponseHeaders(Header[] responseHeaders) {
this.responseHeaders = responseHeaders;
}
public byte[] getByteResult() {
if (byteResult != null) {
return byteResult;
}
if (stringResult != null) {
return stringResult.getBytes();
}
return null;
}
public void setByteResult(byte[] byteResult) {
this.byteResult = byteResult;
}
public String getStringResult() throws UnsupportedEncodingException {
if (stringResult != null) {
return stringResult;
}
if (byteResult != null) {
return new String(byteResult, alipayConfig.getInputCharset());
}
return null;
}
public void setStringResult(String stringResult) {
this.stringResult = stringResult;
}
}
/*
* Alipay.com Inc.
* Copyright (c) 2004-2005 All Rights Reserved.
*/
package io.renren.utils.alipay.httpClient;
/* *
*類名:HttpResultType
*功能:表示Http返回的結果字符方式
*詳細:表示Http返回的結果字符方式
*版本:3.3
*日期:2012-08-17
*說明:
*以下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
*/
public enum HttpResultType {
/**
* 字符串方式
*/
STRING,
/**
* 字節數組方式
*/
BYTES
}
(3)下面是serviceImpl調用接口,發送請求,測試的話可以寫個main方法傳參數即可
/*支付寶海關報文*/
@Override
public Result alipayOffical(Map<String, String> params) throws Exception {
Map<String, String> sParaTemp = new HashMap<>();
sParaTemp.put("service", alipayConfig.getService());
sParaTemp.put("partner", alipayConfig.getPartnerId());
sParaTemp.put("_input_charset", alipayConfig.getInputCharset());
sParaTemp.put("out_request_no", params.get("out_request_no"));
sParaTemp.put("trade_no", params.get("trade_no"));
sParaTemp.put("merchant_customs_code", alipayConfig.getMchCustomsNo());
sParaTemp.put("merchant_customs_name", alipayConfig.getMchCustomsName());
sParaTemp.put("amount", params.get("amount"));
sParaTemp.put("customs_place", alipayConfig.getCustoms());
String createUrl = AlipayUtil.createUrl(sParaTemp, alipayConfig.getPrivateKey(),
alipayConfig.getPayGateWay(), alipayConfig.getInputCharset(), alipayConfig.getIgnType());
HttpProtocolHandler httpProtocolHandler = HttpProtocolHandler.getInstance();
HttpRequest request = new HttpRequest(HttpResultType.STRING);
//設置編碼集
request.setCharset(alipayConfig.getInputCharset());
request.setUrl(createUrl);
System.out.println(request.getUrl());
HttpResponse response = httpProtocolHandler.execute(request, "", "");
String param = response.getStringResult();
System.out.println("支付寶報關響應數據----" + param);
int isSuccessBegin = param.indexOf("<is_success>");
int isSuccessEnd = param.indexOf("</is_success>");
String isSuccess = param.substring(isSuccessBegin + 12, isSuccessEnd);
if ("T".equals(isSuccess)) {
int resultCodeBegin = param.indexOf("<result_code>");
int resultCodeEnd = param.indexOf("</result_code>");
String resultCode = param.substring(resultCodeBegin + 13, resultCodeEnd);
if ("SUCCESS".equals(resultCode)) {
System.out.println("支付寶海關支付報關成功!");
return new Result().ok("SUCCESS");
} else {
System.out.println("支付寶海關支付報關失敗!---請求成功---業務處理失敗");
return new Result().error("FAIL");
}
} else {
System.out.println("支付寶海關支付報關失敗!---請求失敗");
return new Result().error("FAIL");
}
}
(4)業務正常受理並報關成功返回樣例
<?xml version="1.0" encoding="utf-8"?>
<alipay>
<is_success>T</is_success>
<request>
<param name="trade_no">2015051446800462</param>
<param name="merchant_customs_code">hanguo</param>
<param name="sign_type">MD5</param>
<param name="merchant_customs_name">jwyhanguo_card</param>
<param name="sign">2118ac8fad6bc1d9e88a6cd017c18d37</param>
<param name="amount">2</param>
<param name="_input_charset">UTF-8</param>
<param name="customs_place">HANGZHOU</param>
<param name="service">alipay.acquire.customs</param>
<param name="out_request_no">9193457120563834</param>
<param name="partner">2088101142878662</param>
</request>
<response>
<alipay>
<result_code>SUCCESS</result_code>
<trade_no>2013111511001004390000105126</trade_no>
<alipay_declare_no>2013112611001004680073956707</alipay_declare_no>
</alipay>
</response>
<sign>3afc92ac4708425ab74ecb2c4e58ef56</sign>
<sign_type>MD5</sign_type>
</alipay>