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>