|
|
@@ -0,0 +1,367 @@
|
|
|
+package com.java110.acct.payment.adapt.icbc;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.icbc.api.DefaultIcbcClient;
|
|
|
+import com.icbc.api.IcbcApiException;
|
|
|
+import com.icbc.api.IcbcConstants;
|
|
|
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1;
|
|
|
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1;
|
|
|
+import com.icbc.api.utils.HttpRequestParamsUtils;
|
|
|
+import com.icbc.api.utils.IcbcEncrypt;
|
|
|
+import com.icbc.api.utils.IcbcSignature;
|
|
|
+import com.icbc.api.utils.WebUtils;
|
|
|
+import com.java110.acct.payment.IPaymentFactoryAdapt;
|
|
|
+import com.java110.acct.payment.adapt.plutus.PlutusPaymentFactoryAdapt;
|
|
|
+import com.java110.core.context.ICmdDataFlowContext;
|
|
|
+import com.java110.core.factory.CommunitySettingFactory;
|
|
|
+import com.java110.core.factory.PlutusFactory;
|
|
|
+import com.java110.core.factory.WechatFactory;
|
|
|
+import com.java110.core.log.LoggerFactory;
|
|
|
+import com.java110.dto.app.AppDto;
|
|
|
+import com.java110.dto.owner.OwnerAppUserDto;
|
|
|
+import com.java110.dto.payment.PaymentOrderDto;
|
|
|
+import com.java110.dto.smallWeChat.SmallWeChatDto;
|
|
|
+import com.java110.intf.store.ISmallWechatV1InnerServiceSMO;
|
|
|
+import com.java110.intf.user.IOwnerAppUserInnerServiceSMO;
|
|
|
+import com.java110.utils.cache.MappingCache;
|
|
|
+import com.java110.utils.constant.WechatConstant;
|
|
|
+import com.java110.utils.util.*;
|
|
|
+import org.bouncycastle.util.encoders.Base64;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.*;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 工商银行支付
|
|
|
+ *
|
|
|
+ * 这个适配器使用需要 安装 工商银行的 jar 安装到maven
|
|
|
+ * 这里 以免影响 编译 所以这个Java 改名为txt
|
|
|
+ *
|
|
|
+ * 如果需要工商银行支付
|
|
|
+ * 1、将 工商银行提供的 icbc-api-sdk-cop icbc-api-sdk-cop-io icbc-ca 安装到自己的私有仓库 怎么安装请百度
|
|
|
+ *
|
|
|
+ * 2、根据你安装的 命令 会 有如下的三个jar的依赖包,将这三个系诶到 service-acct 下的pom.xml 文件中
|
|
|
+ *
|
|
|
+ * <dependency>
|
|
|
+ <groupId>com.icbc</groupId>
|
|
|
+ <artifactId>icbc-api-sdk-cop</artifactId>
|
|
|
+ <version>v1.0</version>
|
|
|
+ </dependency>
|
|
|
+ <dependency>
|
|
|
+ <groupId>com.icbc</groupId>
|
|
|
+ <artifactId>icbc-api-sdk-cop-io</artifactId>
|
|
|
+ <version>v1.0</version>
|
|
|
+ </dependency>
|
|
|
+ <dependency>
|
|
|
+ <groupId>com.icbc</groupId>
|
|
|
+ <artifactId>icbc-ca</artifactId>
|
|
|
+ <version>v1.0</version>
|
|
|
+ </dependency>
|
|
|
+
|
|
|
+ *
|
|
|
+ * 3、将 此类 文件 名去掉最后的TXT 修改 为Java 后缀
|
|
|
+ *
|
|
|
+ * 4、执行mvn clean install 执行打包测试
|
|
|
+ *
|
|
|
+ * 5、如果要二次开发优化参考 工商银行协议
|
|
|
+ * https://open.icbc.com.cn/icbc/apip/api_detail.html?apiId=10000000000000137000&baseUrl=%2Fcardbusiness%2Faggregatepay%2Fb2c%2Fonline&resUrl=%2Fconsumepurchase&version=V1&apiName=聚合支付B2C线上消费下单&serviceId=P0067&resourceId=10000000000000009800
|
|
|
+ * @desc add by 吴学文 15:33
|
|
|
+ */
|
|
|
+@Service("icbcPaymentFactory")
|
|
|
+public class IcbcPaymentFactoryAdapt implements IPaymentFactoryAdapt {
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(PlutusPaymentFactoryAdapt.class);
|
|
|
+
|
|
|
+
|
|
|
+ //微信支付
|
|
|
+ public static final String DOMAIN_WECHAT_PAY = "WECHAT_PAY";
|
|
|
+ // 微信服务商支付开关
|
|
|
+ public static final String WECHAT_SERVICE_PAY_SWITCH = "WECHAT_SERVICE_PAY_SWITCH";
|
|
|
+
|
|
|
+ //开关ON打开
|
|
|
+ public static final String WECHAT_SERVICE_PAY_SWITCH_ON = "ON";
|
|
|
+
|
|
|
+
|
|
|
+ public static final String TRADE_TYPE_NATIVE = "NATIVE";
|
|
|
+ public static final String TRADE_TYPE_JSAPI = "JSAPI";
|
|
|
+ public static final String TRADE_TYPE_MWEB = "MWEB";
|
|
|
+ public static final String TRADE_TYPE_APP = "APP";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ISmallWechatV1InnerServiceSMO smallWechatV1InnerServiceSMOImpl;
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IOwnerAppUserInnerServiceSMO ownerAppUserInnerServiceSMOImpl;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RestTemplate outRestTemplate;
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map java110Payment(PaymentOrderDto paymentOrderDto, JSONObject reqJson, ICmdDataFlowContext context) throws Exception {
|
|
|
+
|
|
|
+ SmallWeChatDto smallWeChatDto = getSmallWechat(reqJson);
|
|
|
+
|
|
|
+
|
|
|
+ String appId = context.getReqHeaders().get("app-id");
|
|
|
+ String userId = context.getReqHeaders().get("user-id");
|
|
|
+ String tradeType = reqJson.getString("tradeType");
|
|
|
+ String notifyUrl = MappingCache.getValue("OWNER_WECHAT_URL") + "/app/payment/notify/icbc/992020011134400001";
|
|
|
+
|
|
|
+ String openId = reqJson.getString("openId");
|
|
|
+
|
|
|
+ if (StringUtil.isEmpty(openId)) {
|
|
|
+ String appType = OwnerAppUserDto.APP_TYPE_WECHAT_MINA;
|
|
|
+ if (AppDto.WECHAT_OWNER_APP_ID.equals(appId)) {
|
|
|
+ appType = OwnerAppUserDto.APP_TYPE_WECHAT;
|
|
|
+ } else if (AppDto.WECHAT_MINA_OWNER_APP_ID.equals(appId)) {
|
|
|
+ appType = OwnerAppUserDto.APP_TYPE_WECHAT_MINA;
|
|
|
+ } else {
|
|
|
+ appType = OwnerAppUserDto.APP_TYPE_APP;
|
|
|
+ }
|
|
|
+
|
|
|
+ OwnerAppUserDto ownerAppUserDto = new OwnerAppUserDto();
|
|
|
+ ownerAppUserDto.setUserId(userId);
|
|
|
+ ownerAppUserDto.setAppType(appType);
|
|
|
+ List<OwnerAppUserDto> ownerAppUserDtos = ownerAppUserInnerServiceSMOImpl.queryOwnerAppUsers(ownerAppUserDto);
|
|
|
+
|
|
|
+ Assert.listOnlyOne(ownerAppUserDtos, "未找到开放账号信息");
|
|
|
+ openId = ownerAppUserDtos.get(0).getOpenId();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ logger.debug("【小程序支付】 统一下单开始, 订单编号=" + paymentOrderDto.getOrderId());
|
|
|
+ SortedMap<String, String> resultMap = new TreeMap<String, String>();
|
|
|
+ //生成支付金额,开发环境处理支付金额数到0.01、0.02、0.03元
|
|
|
+ double payAmount = PayUtil.getPayAmountByEnv(MappingCache.getValue("HC_ENV"), paymentOrderDto.getMoney());
|
|
|
+ //添加或更新支付记录(参数跟进自己业务需求添加)
|
|
|
+
|
|
|
+ JSONObject resMap = null;
|
|
|
+ resMap = this.java110UnifieldOrder(paymentOrderDto.getName(),
|
|
|
+ paymentOrderDto.getOrderId(),
|
|
|
+ tradeType,
|
|
|
+ payAmount,
|
|
|
+ openId,
|
|
|
+ smallWeChatDto,
|
|
|
+ notifyUrl
|
|
|
+ );
|
|
|
+
|
|
|
+
|
|
|
+ if (TRADE_TYPE_JSAPI.equals(tradeType)) {
|
|
|
+ resultMap.putAll(JSONObject.toJavaObject(resMap, Map.class));
|
|
|
+ resultMap.put("sign", resultMap.get("paySign"));
|
|
|
+ } else if (TRADE_TYPE_APP.equals(tradeType)) {
|
|
|
+ resultMap.put("appId", smallWeChatDto.getAppId());
|
|
|
+ resultMap.put("timeStamp", PayUtil.getCurrentTimeStamp());
|
|
|
+ resultMap.put("nonceStr", PayUtil.makeUUID(32));
|
|
|
+ resultMap.put("partnerid", smallWeChatDto.getMchId());
|
|
|
+ resultMap.put("prepayid", resMap.getString("session_id"));
|
|
|
+ //resultMap.put("signType", "MD5");
|
|
|
+ resultMap.put("sign", PayUtil.createSign(resultMap, smallWeChatDto.getPayPassword()));
|
|
|
+ } else if (TRADE_TYPE_NATIVE.equals(tradeType)) {
|
|
|
+ resultMap.put("prepayId", resMap.getString("session_id"));
|
|
|
+ resultMap.put("codeUrl", resMap.getString("qr_code"));
|
|
|
+ }
|
|
|
+ resultMap.put("code", "0");
|
|
|
+ resultMap.put("msg", "下单成功");
|
|
|
+ logger.info("【小程序支付】统一下单成功,返回参数:" + resultMap);
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private JSONObject java110UnifieldOrder(String feeName, String orderNum,
|
|
|
+ String tradeType, double payAmount, String openid,
|
|
|
+ SmallWeChatDto smallWeChatDto, String notifyUrl) throws Exception {
|
|
|
+
|
|
|
+ //String systemName = MappingCache.getValue(WechatConstant.WECHAT_DOMAIN, WechatConstant.PAY_GOOD_NAME);
|
|
|
+ String privateKey = CommunitySettingFactory.getRemark(smallWeChatDto.getObjId(), "ICBC_PRIVATE_KEY");
|
|
|
+ String apiPublicKey = CommunitySettingFactory.getRemark(smallWeChatDto.getObjId(), "ICBC_PUBLIC_KEY");
|
|
|
+ String merId = CommunitySettingFactory.getValue(smallWeChatDto.getObjId(), "ICBC_MER_ID");
|
|
|
+ String merPrtclNo = CommunitySettingFactory.getValue(smallWeChatDto.getObjId(), "ICBC_MER_PRTCL_NO");
|
|
|
+ String deciveInfo = CommunitySettingFactory.getValue(smallWeChatDto.getObjId(), "ICBC_DECIVE_INFO");
|
|
|
+ String appName = CommunitySettingFactory.getValue(smallWeChatDto.getObjId(), "ICBC_APP_NAME");
|
|
|
+ String icbcAppId = CommunitySettingFactory.getValue(smallWeChatDto.getObjId(), "ICBC_APP_ID");
|
|
|
+
|
|
|
+
|
|
|
+ DefaultIcbcClient client = new DefaultIcbcClient(smallWeChatDto.getAppId(), IcbcConstants.SIGN_TYPE_RSA2, privateKey, apiPublicKey);
|
|
|
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1();
|
|
|
+ //根据测试环境和生产环境替换相应ip和端口
|
|
|
+ request.setServiceUrl("https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/consumepurchase/V1");
|
|
|
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz
|
|
|
+ bizContent = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz();
|
|
|
+ request.setBizContent(bizContent);
|
|
|
+ //请对照接口文档用bizContent.setxxx()方法对业务上送数据进行赋值
|
|
|
+ bizContent.setMer_id(merId);
|
|
|
+ bizContent.setOut_trade_no(orderNum);
|
|
|
+ bizContent.setPay_mode("9");
|
|
|
+ bizContent.setAccess_type("7");
|
|
|
+ bizContent.setMer_prtcl_no(merPrtclNo);
|
|
|
+ bizContent.setOrig_date_time(DateUtil.getNow(DateUtil.DATE_FORMATE_STRING_E).replace("Z", ""));
|
|
|
+ bizContent.setDecive_info(deciveInfo);
|
|
|
+ bizContent.setBody(appName + "-购买商品");
|
|
|
+ bizContent.setFee_type("001");
|
|
|
+ bizContent.setSpbill_create_ip("192.168.1.8");
|
|
|
+ bizContent.setTotal_fee(PayUtil.moneyToIntegerStr(payAmount));
|
|
|
+ bizContent.setMer_url(notifyUrl);
|
|
|
+ bizContent.setShop_appid(smallWeChatDto.getAppId());
|
|
|
+ bizContent.setIcbc_appid(icbcAppId);
|
|
|
+ bizContent.setOpen_id(openid);
|
|
|
+ // bizContent.setMer_acct("6212880200000038618");
|
|
|
+ bizContent.setExpire_time("120");
|
|
|
+ bizContent.setNotify_type("HS");
|
|
|
+ bizContent.setResult_type("1");
|
|
|
+ bizContent.setPay_limit("no_credit");
|
|
|
+ bizContent.setOrder_apd_inf("");
|
|
|
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1 response;
|
|
|
+
|
|
|
+ response = client.execute(request, System.currentTimeMillis() + "");//msgId消息通讯唯一编号,要求每次调用独立生成,APP级唯一
|
|
|
+ if (response.getReturnCode() == 0) {
|
|
|
+ // 6、业务成功处理,请根据接口文档用response.getxxx()获取同步返回的业务数据
|
|
|
+ System.out.println("ReturnCode:" + response.getReturnCode());
|
|
|
+ System.out.println("response:" + JSON.toJSONString(response));
|
|
|
+ return JSONObject.parseObject(response.getWx_data_package());
|
|
|
+ } else {
|
|
|
+ // 失败
|
|
|
+ System.out.println("ReturnMsg:" + response.getReturnMsg());
|
|
|
+ throw new IllegalArgumentException("支付失败" + response.getReturnMsg());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PaymentOrderDto java110NotifyPayment(String param) {
|
|
|
+
|
|
|
+ PaymentOrderDto paymentOrderDto = new PaymentOrderDto();
|
|
|
+ JSONObject json = JSON.parseObject(param);
|
|
|
+
|
|
|
+ String appId;
|
|
|
+ if (json.containsKey("wId")) {
|
|
|
+ String wId = json.get("wId").toString();
|
|
|
+ wId = wId.replace(" ", "+");
|
|
|
+ appId = WechatFactory.getAppId(wId);
|
|
|
+ } else {
|
|
|
+ appId = json.get("appid").toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONObject paramIn = new JSONObject();
|
|
|
+ paramIn.put("appId", appId);
|
|
|
+ SmallWeChatDto smallWeChatDto = getSmallWechat(paramIn);
|
|
|
+ if (smallWeChatDto == null) {
|
|
|
+ throw new IllegalArgumentException("未配置公众号或者小程序信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, String> params=new HashMap<String, String>();
|
|
|
+
|
|
|
+ params.put("from", json.getString("from"));
|
|
|
+ params.put("api", json.getString("api"));
|
|
|
+ params.put("app_id", json.getString("app_id"));
|
|
|
+ params.put("charset", json.getString("charset"));
|
|
|
+ params.put("format", json.getString("format"));
|
|
|
+ params.put("encrypt_type", json.getString("encrypt_type"));
|
|
|
+ params.put("timestamp",json.getString("timestamp") );
|
|
|
+ params.put("biz_content",json.getString("biz_content") );
|
|
|
+ params.put("sign_type", json.getString("sign_type"));//目前上行网关签名暂时仅支持RSA
|
|
|
+
|
|
|
+ String path="/app/payment/notify/icbc/992020011134400001\"";
|
|
|
+ String signStr= WebUtils.buildOrderedSignStr(path, params);
|
|
|
+ String results = null;
|
|
|
+ String responseBizContent= null;
|
|
|
+
|
|
|
+
|
|
|
+ String apiPublicKey = CommunitySettingFactory.getRemark(smallWeChatDto.getObjId(), "ICBC_PUBLIC_KEY");
|
|
|
+ String theKey = CommunitySettingFactory.getValue(smallWeChatDto.getObjId(), "ICBC_KEY");
|
|
|
+ String privateKey = CommunitySettingFactory.getRemark(smallWeChatDto.getObjId(), "ICBC_PRIVATE_KEY");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ boolean flag= false;
|
|
|
+ String content = "";
|
|
|
+ try {
|
|
|
+ flag = IcbcSignature.verify(signStr, json.getString("sign_type"), apiPublicKey, json.getString("charset"), json.getString("sign"));
|
|
|
+ } catch (IcbcApiException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ if (!flag) {
|
|
|
+ responseBizContent= "{\"return_code\":-12345,\"return_msg\":\"icbc sign not pass.\"}";
|
|
|
+ }else
|
|
|
+ {
|
|
|
+
|
|
|
+ /**********biz_content解密**********/
|
|
|
+ if ("AES".equals(json.getString("encrypt_type")))
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ content= IcbcEncrypt.decryptContent( json.getString("biz_content"), json.getString("encrypt_type"), theKey, json.getString("charset"));
|
|
|
+ } catch (IcbcApiException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**********合作方/分行 业务逻辑处理**********/
|
|
|
+ JSONObject map = JSONObject.parseObject(content);
|
|
|
+ logger.info("【iccb支付回调】 回调数据: \n" + map);
|
|
|
+ //更新数据
|
|
|
+ paymentOrderDto.setOrderId(map.getString("out_trade_no"));
|
|
|
+ paymentOrderDto.setTransactionId(map.getString("order_id"));
|
|
|
+
|
|
|
+ String msg_id=map.get("msg_id").toString();
|
|
|
+ //业务返回参数设置
|
|
|
+ int return_code=0;
|
|
|
+ String return_msg="success.";
|
|
|
+ responseBizContent="{\"return_code\":"+return_code+",\"return_msg\":\""+return_msg+"\",\"msg_id\":\""+msg_id+"\","
|
|
|
+ +"\"busi_param_rp\":\"thisisresponseparameter\"}";
|
|
|
+
|
|
|
+ /**********response_biz_content加密**********/
|
|
|
+
|
|
|
+ if ("AES".equals(json.getString("encrypt_type")))
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ responseBizContent=IcbcEncrypt.encryptContent(responseBizContent, json.getString("encrypt_type"), theKey, json.getString("charset"));
|
|
|
+ } catch (IcbcApiException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ responseBizContent="\""+responseBizContent+"\"";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ signStr="\"response_biz_content\":"+responseBizContent+","+"\"sign_type\":"+"\"RSA\"";
|
|
|
+ String sign= null;
|
|
|
+ try {
|
|
|
+ sign = IcbcSignature.sign(signStr, "RSA", privateKey, json.getString("charset"),theKey);
|
|
|
+ } catch (IcbcApiException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ results="{"+signStr+",\"sign\":\""+sign+"\"}";
|
|
|
+
|
|
|
+ paymentOrderDto.setResponseEntity(new ResponseEntity<String>(results, HttpStatus.OK));
|
|
|
+ return paymentOrderDto;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private SmallWeChatDto getSmallWechat(JSONObject paramIn) {
|
|
|
+
|
|
|
+ SmallWeChatDto smallWeChatDto = new SmallWeChatDto();
|
|
|
+ smallWeChatDto.setObjId(paramIn.getString("communityId"));
|
|
|
+ smallWeChatDto.setAppId(paramIn.getString("appId"));
|
|
|
+ smallWeChatDto.setPage(1);
|
|
|
+ smallWeChatDto.setRow(1);
|
|
|
+ List<SmallWeChatDto> smallWeChatDtos = smallWechatV1InnerServiceSMOImpl.querySmallWechats(smallWeChatDto);
|
|
|
+
|
|
|
+ if (smallWeChatDtos == null || smallWeChatDtos.size() < 1) {
|
|
|
+ smallWeChatDto = new SmallWeChatDto();
|
|
|
+ smallWeChatDto.setAppId(MappingCache.getValue(WechatConstant.WECHAT_DOMAIN, "appId"));
|
|
|
+ smallWeChatDto.setAppSecret(MappingCache.getValue(WechatConstant.WECHAT_DOMAIN, "appSecret"));
|
|
|
+ smallWeChatDto.setMchId(MappingCache.getValue(WechatConstant.WECHAT_DOMAIN, "mchId"));
|
|
|
+ smallWeChatDto.setPayPassword(MappingCache.getValue(WechatConstant.WECHAT_DOMAIN, "key"));
|
|
|
+ return smallWeChatDto;
|
|
|
+ }
|
|
|
+
|
|
|
+ return BeanConvertUtil.covertBean(smallWeChatDtos.get(0), SmallWeChatDto.class);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|