Java实现建行聚合支付对接及其回调

本文详细介绍了如何使用Java实现建设银行聚合支付的对接,包括订单生成、支付回调处理,并提供了相关实体类和工具类的代码示例。回调处理包括页面回调和服务器回调,涉及验签等安全措施。同时,文章提及了支付状态的二次校验问题,欢迎成功解决者交流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java实现建行聚合支付对接及其回调


前言

本文原文基础上,进行完善而成
原文链接 java实现对接建行支付及其回调
其他可参考链接 集成建行聚合支付踩过的坑,有些槽不吐不快

产生订单

	/**
    * 验签用,相关包找银行要
    */
   private RSASig rsaSig = new RSASig();

    /**
     * 商户代码,固定写死的,找银行客户经理要
     */
    @Value("${ccb.MERCHANTID}")
    private String MERCHANTID;
    /**
     * 商户柜台代码, 这里用yml会报错,固定写死即可,找银行客户经理要
     */
    private String POSID = "xxxxxxxxx";
    /**
     * 分行代码,固定写死的,找银行客户经理要
     */
    @Value("${ccb.BRANCHID}")
    private String BRANCHID;
    /**
     * 完整公钥,验签用,建行官网"商户服务平台"下载"E路护航"获取
     */
    @Value("${ccb.PUBLICKEY}")
    private String PUBLICKEY;
    /**
     * 公钥后30位
     */
    @Value("${ccb.PUB}")
    private String PUB;
    /**
     * 交易码 这个参数的值是固定的,不可以修改
     */
    @Value("${ccb.TXCODE}")
    private String TXCODE;

    @ResponseBody
    @PostMapping("ccbPay")
    public JSONObject ccbPay(String id, String money) {
        /**
         * 生成线上订单
         */
        String PAYMENT = money;
        StringBuffer tmp = new StringBuffer();
        tmp.append("MERCHANTID=").append(MERCHANTID);
        tmp.append("&POSID=").append(POSID);
        tmp.append("&BRANCHID=").append(BRANCHID);
        tmp.append("&ORDERID=").append(ORDERID);
        tmp.append("&PAYMENT=").append(PAYMENT);
        tmp.append("&CURCODE=01");
        tmp.append("&TXCODE=").append(TXCODE);
        tmp.append("&REMARK1=&REMARK2=");
        tmp.append("&RETURNTYPE=3").append("&TIMEOUT=");
        tmp.append("&PUB=").append(PUB);
        Map map = new HashMap();
        map.put("CCB_IBSVersion", "V6");//必输项
        map.put("MERCHANTID", MERCHANTID);
        map.put("BRANCHID", BRANCHID);
        map.put("POSID", POSID);
        map.put("ORDERID", ORDERID);
        map.put("PAYMENT", PAYMENT);
        map.put("CURCODE", "01");
        map.put("TXCODE", TXCODE);
        map.put("REMARK1", "");
        map.put("REMARK2", "");
        map.put("RETURNTYPE", "3");
        map.put("TIMEOUT", "");
        map.put("MAC", MD5.md5Str(tmp.toString()));
        // 这个url是建设银行指定的,尽量不要换
        String ret = HttpClientUtil.httpPost("https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6", map);

        QrURLDemo qrURLDemo = JSON.parseObject(ret, QrURLDemo.class);
        // 这个url触发get请求会获取到一个新的页面
        String s = HttpClientUtil.httpGet(qrURLDemo.getPAYURL(), "UTF-8");

        // 获取QRURL
        QrURLDemo qrURLDemo1 = JSON.parseObject(s, QrURLDemo.class);
        String decode = "";
        try {
            decode = URLDecoder.decode(qrURLDemo1.getQRURL(), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        String code = qrURLDemo1.getSUCCESS();
        
 		JSONObject json = new JSONObject();
        // 安卓通过这个url就可以支付了
        json.put("decode", decode); //返回的这个decode就是支付所需跳转的url
        json.put("code", code); //这个是状态码
        // 还会返回其他东西,可打断点,按需取
        return json;
    }

yml配置文件

ccb:
  MERCHANTID: # 商户代码,固定写死的,找银行客户经理要
  BRANCHID: # 分行代码,固定写死的,找银行客户经理要
  TXCODE: 530550  # 根据支付方式,写死
  PUB: # 公钥后30位
  PUBLICKEY: # 完整公钥

说明

decode:就是支付二维码的链接。可在微信、支付宝中直接跳转支付页面,也可转换为二维码扫码跳转支付页面

银行回调

支付完成后,建行会自动调用回调地址(在建行官网商户平台配置,银行的客户经理也能配置),分为页面回调和服务器回调

  • 页面反馈(方法:get):付款人付款完成后,点击“返回商户网站”按钮,触发页面反馈

  • 服务器反馈(方法:post):只要支付成功,无需触发,由建行支付网关,以post 方法,发信息给反馈URL

  /**
     * 支付页面回调(页面反馈 get)付款人付款完成后,点击“返回商户网站”按钮,触发页面反馈
     *
     * @return
     */
    @GetMapping("/payCallBackForPage")
    @ResponseBody
    public String payCallBackForPage(PayCallBackEntity payCallBackEntity, HttpServletResponse response) throws Exception {
//      本人直接忽略页面回调,需要的话和服务器回调代码类似
        return "SUCCESS";
    }

 /**
     * 支付服务器回调(服务器反馈 Post)付款人付款完成后,触发服务器反馈
     *
     * @return
     */
    @PostMapping("/payCallBackForServer")
    @ResponseBody
    public String payCallBackForServer(PayCallBackEntity payCallBackEntity, HttpServletResponse response) throws Exception {
        System.out.println("payCallBackEntity = " + payCallBackEntity);
        // 验签
        rsaSig.setPublicKey(PUBLICKEY);
        String src = "POSID=" + payCallBackEntity.getPOSID() + "&BRANCHID=" + payCallBackEntity.getBRANCHID() + "&ORDERID=" + payCallBackEntity.getORDERID()
                + "&PAYMENT=" + payCallBackEntity.getPAYMENT() + "&CURCODE=" + payCallBackEntity.getCURCODE() + "&REMARK1=" + payCallBackEntity.getREMARK1()
                + "&REMARK2=" + payCallBackEntity.getREMARK2() + "&ACC_TYPE=" + payCallBackEntity.getACC_TYPE() + "&SUCCESS=" + payCallBackEntity.getSUCCESS();
        // 验签结果
        boolean signResult = rsaSig.verifySigature(payCallBackEntity.getSIGN(), src);
        if (!signResult) {
            System.out.println("验签失败!");
            return "SUCCESS";
        }
        String success = payCallBackEntity.getSUCCESS();
        String orderId = payCallBackEntity.getORDERID();
        String money = payCallBackEntity.getPAYMENT();
        System.out.println("success: -" + success);
        System.out.println("orderId: -" + orderId);
        if ("Y".equals(success)) {
            // 此处最好调用查询接口获取XML文件,然后通过XML校验是否支付成功,本人尝试多次未果,有兴趣可参考前言中"原文链接"尝试
            // 更新运单中的支付状态  更新支付状态  记录收款日志
            // 其他操作自行添加
         } else {
              System.out.println("支付失败");
         }
        // 不论支付成功失败,给银行一个返回结果
        return "SUCCESS";
    }

支付回调实体类

@Data
public class PayCallBackEntity{
    private String POSID; //商户柜台代码
    private String BRANCHID;//分行代码
    private String ORDERID; //定单号
    private String PAYMENT; //付款金额
    private String CURCODE;  //币种
    private String REMARK1;  //备注一
    private String REMARK2;  //备注二
    private String ACC_TYPE;  //账户类型  服务器通知中有此字段返回且参与验签
    private String SUCCESS;  //成功标志  成功-Y,失败-N
    private String TYPE;   //接口类型  分行业务人员在P2员工渠道后台设置防钓鱼的开关。 1.开关关闭时,无此字段返回且不参与验签 2.开关打开时,有此字段返回且参与验签。参数值为 1-防钓鱼接口

    private String REFERER;  //Referer信息  分行业务人员在P2员工渠道后台设置防钓鱼开关。 1.开关关闭时,无此字段返回且不参与验签。 2.开关打开时,有此字段返回且参与验签
    private String CLIENTIP;  //客户端IP  分行业务人员在P2员工渠道后台设置防钓鱼的开关。 1.开关关闭时,无此字段返回且不参与验签 2.开关打开时,有此字段返回且参与验签。参数值为 客户在建行系统中的IP
    private String ACCDATE;  //系统记账日期  商户登陆商户后台设置返回记账日期的开关 1.开关关闭时,无此字段返回且不参与验签。 2.开关打开时,有此字段返回且参与验签。参数值格式为YYYYMMDD(如20100907)。
    private String USRMSG; //支付账户信息  分行业务人员在P2员工渠道后台设置防钓鱼开关和返回账户信息的开关。 1.开关关闭时,无此字段返回且不参与验签。2.开关打开但支付失败时,无此字段返回且不参与验签。3.开关打开且支付成功时,有此字段返回且参与验签。无PAYTYPE返回时,参数值格式如下:“姓名|账号加密后的密文”。有PAYTYPE返回时,该参数值为空。
    private String USRINFO;   //客户加密信息   分行业务人员在P2员工渠道后台设置防钓鱼开关和客户信息加密返回的开关。 1.开关关闭时,无此字段返回且不参与验签
    private String PAYTYPE;  //支付方式   ALIPAY:支付宝 WEIXIN:微信 为空:建行龙支付 该字段有返回时参与验签,无此字段返回时不参与验签。
    private String SIGN;  //数字签名
}

httpclient工具类

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

public class HttpClientUtil {


	public static String httpReader(String url, String code){
		System.out.println("GetPage:"+url);
		
		HttpClient client = new HttpClient();
		GetMethod method = new GetMethod(url);
		
		String result = null;
		try {
			client.executeMethod(method);
			int status = method.getStatusCode();
			if (status == HttpStatus.SC_OK) {
				result = method.getResponseBodyAsString();
			} else {
				System.out.println("Method failed: " + method.getStatusLine());
			}
		} catch (HttpException e) {
			System.out.println("Please check your provided http address!");
			e.printStackTrace();
		} catch (IOException e) {
		
			e.printStackTrace();
		} finally{
			if(method!=null)method.releaseConnection();
			method = null;
			client = null;
		}
		return result;
	}
	
	public static String httpGet(String url,String code) {
		System.out.println("GetPage:"+url);
		String content = null;
		HttpClient httpClient = new HttpClient();
		httpClient.getParams().setParameter(HttpMethodParams.USER_AGENT,"Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.2) Gecko/20090803 Fedora/3.5.2-2.fc11 Firefox/3.5.2");
		GetMethod method = new GetMethod(url);
		try {
			int statusCode = httpClient.executeMethod(method);
			System.out.println("httpClientUtils::statusCode="+statusCode);
			System.out.println(method.getStatusLine());
			content = new String(method.getResponseBody(), code);
			
		} catch (Exception e) {
			System.out.println("time out");
			e.printStackTrace();
		} finally {
			if(method!=null)method.releaseConnection();
			method = null;
			httpClient = null;
		}
		return content;
	}
	
	public static String httpPost(String url, Map paramMap, String code) {
		System.out.println("GetPage:"+url);
		String content = null;
		if (url == null || url.trim().length() == 0 || paramMap == null
				|| paramMap.isEmpty())
			return null;
		HttpClient httpClient = new HttpClient();

		httpClient.getParams().setParameter(HttpMethodParams.USER_AGENT,"Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.2) Gecko/20090803 Fedora/3.5.2-2.fc11 Firefox/3.5.2");//

		
		PostMethod method = new PostMethod(url);
		Iterator it = paramMap.keySet().iterator();
		

		while (it.hasNext()) {
			String key = it.next() + "";
			Object o = paramMap.get(key);
			if (o != null && o instanceof String) {
				method.addParameter(new NameValuePair(key, o.toString()));
			}
			if (o != null && o instanceof String[]) {
				String[] s = (String[]) o;
				if (s != null)
					for (int i = 0; i < s.length; i++) {
						method.addParameter(new NameValuePair(key, s[i]));
					}
			}
		}
		try {
			
			int statusCode = httpClient.executeMethod(method);
			
			System.out.println("httpClientUtils::statusCode="+statusCode);

			System.out.println(method.getStatusLine());
			content = new String(method.getResponseBody(), code);
			
		} catch (Exception e) {
			System.out.println("time out");
			e.printStackTrace();
		} finally {
			if(method!=null)method.releaseConnection();
			method = null;
			httpClient = null;
		}
		return content;

	}

	public static String httpPost(String url, Map paramMap) {

		return HttpClientUtil.httpPost(url, paramMap, "UTF-8");
	}
}

MD5工具类

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5 {

	public static String md5Str(String str) {
		if (str == null) return "";
		return md5Str(str, 0);
	}

	public static String md5Str(String str, int offset) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			byte[] b = str.getBytes("UTF8");
			md5.update(b, offset, b.length);
			return byteArrayToHexString(md5.digest());
		} catch (NoSuchAlgorithmException ex) {
			ex.printStackTrace();
			return null;
		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
			return null;
		}
	}

	/**
	 * @param b byte[]
	 * @return String
	 */
	public static String byteArrayToHexString(byte[] b) {
		String result = "";
		for (int i = 0; i < b.length; i++) {
			result += byteToHexString(b[i]);
		}
		return result;
	}

	private static String[] hexDigits =
			{
					"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b",
					"c", "d", "e", "f"};


	public static String byteToHexString(byte b) {
		int n = b;
		if (n < 0) {
			n = 256 + n;
		}
		int d1 = n / 16;
		int d2 = n % 16;
		return hexDigits[d1] + hexDigits[d2];
	}
}

建行无感支付实体类

/**
 * 建行无感支付实体类
 */
public class QrURLDemo {

	private String SUCCESS;
	private String PAYURL;
	private String QRURL;  //安卓点这个会直接跳到支付页面
}

后记

  • 关于查询接口获取xml文件,然后进行二次校验的操作,有成功的大佬请私信或留言交流一下,本人尝试多次未果
  • 其他问题留言
实现建设银行聚合支付生成订单接口,可以按照以下步骤进行: 1. 导入建设银行支付接口的开发包。在Java项目中,可以通过使用Maven或Gradle等构建工具,将建设银行支付接口的依赖添加到项目中。 2. 创建一个Java类来处理生成订单的逻辑。可以命名为"CcbPaymentService"或类似的名称。在该类中,可以定义一个方法名为"generateOrder"用于生成订单。 3. 在"generateOrder"方法中,首先要通过建设银行支付接口提供的API,构建生成订单的请求参数。这些参数通常包括商户号、支付金额、订单号、交易描述等。 4. 调用建设银行支付接口的生成订单方法,将构建好的请求参数传递给该方法。可以通过使用Java的网络请求库,如HttpClient或OkHttp,向建设银行支付接口发送POST请求。 5. 接收建设银行支付接口返回的结果,并对结果进行处理。可以通过解析返回的JSON数据,获取生成的订单号或其他相关信息。 6. 返回生成的订单号或其他相关信息给调用方。可以将该信息封装成一个Java对象,方便后续的使用和传递。 7. 在调用方的其他逻辑中,可以使用这个生成的订单号来进行后续的支付流程。 以上是实现建设银行聚合支付生成订单接口的大致步骤。具体的实现细节会根据建设银行支付接口的具体要求而有所不同。在实际开发中,还需要进行异常处理、参数校验、日志记录等工作,以确保代码的健壮性和稳定性。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值