| | |
| | | package cc.mrbird.febs.pay.service.impl; |
| | | |
| | | import cc.mrbird.febs.common.enumerates.FlowTypeEnum; |
| | | import cc.mrbird.febs.common.enumerates.OrderStatusEnum; |
| | | import cc.mrbird.febs.mall.entity.MallMqRecord; |
| | | import cc.mrbird.febs.mall.entity.MallOrderInfo; |
| | | import cc.mrbird.febs.mall.mapper.MallMqRecordMapper; |
| | | import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper; |
| | | import cc.mrbird.febs.mall.service.ICommonService; |
| | | import cc.mrbird.febs.pay.model.LaKaLaBasicReqDate; |
| | | import cc.mrbird.febs.pay.model.LaKaLaCreateOrderReqDate; |
| | | import cc.mrbird.febs.pay.service.LaKaLaService; |
| | | import cc.mrbird.febs.rabbit.producter.AgentProducer; |
| | | import cn.hutool.core.date.DateUtil; |
| | | import cn.hutool.json.JSONObject; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.codec.binary.Base64; |
| | | import org.apache.commons.io.IOUtils; |
| | |
| | | import org.apache.http.conn.ssl.SSLSocketFactory; |
| | | import org.apache.http.entity.StringEntity; |
| | | import org.apache.http.impl.client.DefaultHttpClient; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.core.io.ClassPathResource; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.net.ssl.SSLContext; |
| | |
| | | import java.security.cert.*; |
| | | import java.security.spec.InvalidKeySpecException; |
| | | import java.security.spec.PKCS8EncodedKeySpec; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | public class LaKaLaServiceImpl implements LaKaLaService { |
| | | |
| | | @Autowired |
| | | private ICommonService commonService; |
| | | |
| | | @Autowired |
| | | private MallOrderInfoMapper orderInfoMapper; |
| | | |
| | | @Autowired |
| | | private MallMqRecordMapper mallMqRecordMapper; |
| | | |
| | | @Autowired |
| | | private AgentProducer agentProducer; |
| | | |
| | | @Autowired |
| | | ResourceLoader resourceLoader; |
| | | |
| | | @Value("${spring.profiles.active}") |
| | | private String active; |
| | | |
| | | /** |
| | | * 字符集固定 utf-8 |
| | |
| | | * 接入 appid |
| | | */ |
| | | public static final String appid = "OP00000418"; |
| | | // public static final String appid = "800000010334001"; |
| | | |
| | | public static final String appid_prd = "OP00000418"; |
| | | |
| | | /** |
| | | * 商户证书序列号,和商户私钥对应 |
| | | */ |
| | | // public static final String mchSerialNo = "017d6ae9ad6e"; |
| | | public static final String mchSerialNo = "01848940fd41"; |
| | | |
| | | public static final String mchSerialNo_prd = "01848940fd41"; |
| | | |
| | | /** |
| | | * 商户号 |
| | | */ |
| | | public static final String merchant_no = "8222900701107M5"; |
| | | |
| | | public static final String merchant_no_prd = "8222900701107M5"; |
| | | |
| | | /** |
| | | * 商户证书私钥,用于请求签名 |
| | | */ |
| | | public static final String merchantPrivateKeyPath = "./src/api_private_key.pem"; |
| | | public static final String merchantPrivateKeyPathName = "classpath:lkl/api_private_key.pem"; |
| | | |
| | | public static final String merchantPrivateKeyPathName_prd = "classpath:lkl/prd/api_private_key.pem"; |
| | | |
| | | /** |
| | | * 拉卡拉公钥证书,用于response签名验证,务必区分测试环境和生产环境证书 |
| | | */ |
| | | public static final String lklCertificatePath = "./src/lkl-apigw-v2.cer"; |
| | | public static final String lklCertificatePathName = "classpath:lkl/lkl-apigw-v2.cer"; |
| | | |
| | | public static final String lklCertificatePathName_prd = "classpath:lkl/prd/lkl-apigw-v2.cer"; |
| | | |
| | | /** |
| | | * api请求地址 |
| | | */ |
| | | public final static String apiUrl = "https://test.wsmsd.cn/sit"; |
| | | public final static String apiUrlCreate = "https://test.wsmsd.cn/sit/api/v3/ccss/counter/order/create"; |
| | | |
| | | public final static String apiUrlCreate_prd = "https://s2.lakala.com/api/v3/ccss/counter/order/create"; |
| | | /** |
| | | * 支付回调地址 |
| | | */ |
| | | public final static String notify_url = "http://120.27.238.55:8185/api/pay/laKaLaPayCallBack"; |
| | | |
| | | public final static String notify_url_prd = "https://hlm.meiao.biz/api/pay/laKaLaPayCallBack"; |
| | | |
| | | private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| | | |
| | | private static final SecureRandom RANDOM = new SecureRandom(); |
| | | |
| | | |
| | | @Override |
| | | public boolean verifyCreateOrder() { |
| | | try { |
| | | String apiPath = "/api/v3/labs/trans/micropay"; |
| | | String body = "{\n" + |
| | | " \"req_time\": \"20210907150256\",\n" + |
| | | " \"version\": \"3.0\",\n" + |
| | | " \"out_org_code\": \"OP00000003\",\n" + |
| | | " \"req_data\": {\n" + |
| | | " \"merchant_no\": \"8222900701107M5\",\n" + |
| | | " \"term_no\": \"A1062976\",\n" + |
| | | " \"out_trade_no\": \"FD660E1FAA3A4470933CDEDAE1EC1D8E\",\n" + |
| | | " \"auth_code\": \"135178236713755038\",\n" + |
| | | " \"total_amount\": \"23\",\n" + |
| | | " \"location_info\": {\n" + |
| | | " \"request_ip\": \"10.176.1.192\",\n" + |
| | | " \"location\": \"+37.123456789,-121.123456789\"\n" + |
| | | " },\n" + |
| | | " \"out_order_no\": \"08F4542EEC6A4497BC419161747A92FA1\"\n" + |
| | | " }\n" + |
| | | "}"; |
| | | public String verifyCreateOrder(LaKaLaCreateOrderReqDate laKaLaCreateOrderReqDateInfo) { |
| | | /** |
| | | * {"req_time":"20221026100323", |
| | | * "version":"1.0", |
| | | * "req_data":{ |
| | | * "out_order_no":"GHSNVDY8033038232443530", |
| | | * "merchant_no":"8222900701107M5", |
| | | * "vpos_id":"627872798014865408", |
| | | * "total_amount":1, |
| | | * "order_efficient_time":"20221118220823", |
| | | * "notify_url":"", |
| | | * "support_cancel":0, |
| | | * "support_refund":1, |
| | | * "support_repeat_pay":1, |
| | | * "order_info":"测试"}} |
| | | */ |
| | | HashMap<String, Object> objectObjectHashMap = new HashMap<>(); |
| | | String yyyyMMddHHmmss = DateUtil.format(new Date(), "yyyyMMddHHmmss"); |
| | | //过期时间 |
| | | String order_efficient_time = DateUtil.format(DateUtil.offsetHour(new Date(),1), "yyyyMMddHHmmss"); |
| | | LaKaLaCreateOrderReqDate laKaLaCreateOrderReqDate = new LaKaLaCreateOrderReqDate(); |
| | | laKaLaCreateOrderReqDate.setOut_order_no(laKaLaCreateOrderReqDateInfo.getOut_order_no()); |
| | | laKaLaCreateOrderReqDate.setTotal_amount(laKaLaCreateOrderReqDateInfo.getTotal_amount()); |
| | | laKaLaCreateOrderReqDate.setOrder_efficient_time(order_efficient_time); |
| | | |
| | | if ("dev".equals(active) || "test".equals(active)) { |
| | | laKaLaCreateOrderReqDate.setNotify_url(notify_url); |
| | | laKaLaCreateOrderReqDate.setMerchant_no(merchant_no); |
| | | }else{ |
| | | laKaLaCreateOrderReqDate.setNotify_url(notify_url_prd); |
| | | laKaLaCreateOrderReqDate.setMerchant_no(merchant_no_prd); |
| | | } |
| | | laKaLaCreateOrderReqDate.setSupport_cancel(0); |
| | | laKaLaCreateOrderReqDate.setSupport_refund(1); |
| | | laKaLaCreateOrderReqDate.setSupport_repeat_pay(1); |
| | | laKaLaCreateOrderReqDate.setOrder_info(laKaLaCreateOrderReqDateInfo.getOrder_info()); |
| | | |
| | | LaKaLaBasicReqDate laKaLaBasicReqDate = new LaKaLaBasicReqDate(); |
| | | laKaLaBasicReqDate.setReq_time(yyyyMMddHHmmss); |
| | | laKaLaBasicReqDate.setVersion("3.0"); |
| | | JSONObject jsonObject = JSONUtil.parseObj(laKaLaCreateOrderReqDate); |
| | | laKaLaBasicReqDate.setReq_data(jsonObject); |
| | | try { |
| | | String body = JSONUtil.parseObj(laKaLaBasicReqDate).toString(); |
| | | |
| | | String authorization = getAuthorization(body); |
| | | HttpResponse response = post(apiUrl + apiPath, body, authorization); |
| | | |
| | | String apiUrlCreateHttp = ""; |
| | | if ("dev".equals(active) || "test".equals(active)) { |
| | | apiUrlCreateHttp = apiUrlCreate; |
| | | }else{ |
| | | apiUrlCreateHttp = apiUrlCreate_prd; |
| | | } |
| | | HttpResponse response = post(apiUrlCreateHttp , body, authorization); |
| | | if (response.getStatusLine().getStatusCode() != 200) { |
| | | System.out.println("请求失败,statusCode " + response.getStatusLine() |
| | | + IOUtils.toString(response.getEntity().getContent(), ENCODING)); |
| | | return false; |
| | | objectObjectHashMap.put("code","false"); |
| | | return JSONUtil.parseObj(objectObjectHashMap).toString(); |
| | | } |
| | | |
| | | String appid = getHeadValue(response, "Lklapi-Appid"); |
| | |
| | | String nonce = getHeadValue(response, "Lklapi-Nonce"); |
| | | String signature = getHeadValue(response, "Lklapi-Signature"); |
| | | String responseStr = IOUtils.toString(response.getEntity().getContent(), ENCODING); |
| | | |
| | | System.out.println("responseStr " + responseStr); |
| | | |
| | | String source = appid + "\n" + lklapiSerial + "\n" + timestamp + "\n" + nonce + "\n" + responseStr + "\n"; |
| | | |
| | | System.out.println("source " + source); |
| | | |
| | | X509Certificate lklCertificate = loadCertificate(new FileInputStream(new File(lklCertificatePath))); |
| | | |
| | | String lklCertificatePathNameStr = ""; |
| | | if ("dev".equals(active) || "test".equals(active)) { |
| | | lklCertificatePathNameStr = lklCertificatePathName; |
| | | }else{ |
| | | lklCertificatePathNameStr = lklCertificatePathName_prd; |
| | | } |
| | | InputStream fileInputStream = resourceLoader.getResource(lklCertificatePathNameStr).getInputStream(); |
| | | X509Certificate lklCertificate = loadCertificate(fileInputStream); |
| | | boolean verify = verify(lklCertificate, source.getBytes(ENCODING), signature); |
| | | |
| | | if (verify) { |
| | | System.out.println("验签成功"); |
| | | return true; |
| | | JSONObject parseObj = JSONUtil.parseObj(responseStr); |
| | | String code = parseObj.get("code").toString(); |
| | | if("000000".equals(code)){ |
| | | Object respData = parseObj.get("resp_data"); |
| | | JSONObject respDataChild = JSONUtil.parseObj(respData); |
| | | String counterUrl = respDataChild.get("counter_url").toString(); |
| | | String payOrderNo = respDataChild.get("pay_order_no").toString(); |
| | | objectObjectHashMap.put("code","true"); |
| | | objectObjectHashMap.put("counterUrl",counterUrl); |
| | | objectObjectHashMap.put("payOrderNo",payOrderNo); |
| | | }else{ |
| | | objectObjectHashMap.put("code","false"); |
| | | } |
| | | return JSONUtil.parseObj(objectObjectHashMap).toString(); |
| | | } else { |
| | | System.err.println("验签失败"); |
| | | return false; |
| | | objectObjectHashMap.put("code","false"); |
| | | return JSONUtil.parseObj(objectObjectHashMap).toString(); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return false; |
| | | objectObjectHashMap.put("code","false"); |
| | | return JSONUtil.parseObj(objectObjectHashMap).toString(); |
| | | } |
| | | |
| | | // public static void main(String[] args) { |
| | | // try { |
| | | // String apiPath = "/api/v3/labs/trans/micropay"; |
| | | // |
| | | // String body = "{\n" + |
| | | // " \"req_time\": \"20210907150256\",\n" + |
| | | // " \"version\": \"3.0\",\n" + |
| | | // " \"out_org_code\": \"OP00000003\",\n" + |
| | | // " \"req_data\": {\n" + |
| | | // " \"merchant_no\": \"822290070111135\",\n" + |
| | | // " \"term_no\": \"29034705\",\n" + |
| | | // " \"out_trade_no\": \"FD660E1FAA3A4470933CDEDAE1EC1D8E\",\n" + |
| | | // " \"auth_code\": \"135178236713755038\",\n" + |
| | | // " \"total_amount\": \"123\",\n" + |
| | | // " \"location_info\": {\n" + |
| | | // " \"request_ip\": \"10.176.1.192\",\n" + |
| | | // " \"location\": \"+37.123456789,-121.123456789\"\n" + |
| | | // " },\n" + |
| | | // " \"out_order_no\": \"08F4542EEC6A4497BC419161747A92FA\"\n" + |
| | | // " }\n" + |
| | | // "}"; |
| | | // |
| | | // |
| | | // String authorization = getAuthorization(body); |
| | | // HttpResponse response = post(apiUrl + apiPath, body, authorization); |
| | | // if (response.getStatusLine().getStatusCode() != 200) { |
| | | // System.out.println("请求失败,statusCode " + response.getStatusLine() |
| | | // + IOUtils.toString(response.getEntity().getContent(), ENCODING)); |
| | | // return; |
| | | // } |
| | | // |
| | | // String appid = getHeadValue(response, "Lklapi-Appid"); |
| | | // String lklapiSerial = getHeadValue(response, "Lklapi-Serial"); |
| | | // String timestamp = getHeadValue(response, "Lklapi-Timestamp"); |
| | | // String nonce = getHeadValue(response, "Lklapi-Nonce"); |
| | | // String signature = getHeadValue(response, "Lklapi-Signature"); |
| | | // String responseStr = IOUtils.toString(response.getEntity().getContent(), ENCODING); |
| | | // |
| | | // System.out.println("responseStr " + responseStr); |
| | | // |
| | | // String source = appid + "\n" + lklapiSerial + "\n" + timestamp + "\n" + nonce + "\n" + responseStr + "\n"; |
| | | // |
| | | // System.out.println("source " + source); |
| | | // |
| | | // X509Certificate lklCertificate = loadCertificate(new FileInputStream(new File(lklCertificatePath))); |
| | | // |
| | | // boolean verify = verify(lklCertificate, source.getBytes(ENCODING), signature); |
| | | // |
| | | // if (verify) { |
| | | // System.out.println("验签成功"); |
| | | // |
| | | // // TODO: 业务处理 |
| | | // |
| | | // } else { |
| | | // System.err.println("验签失败"); |
| | | // } |
| | | // |
| | | // } catch (Exception e) { |
| | | // e.printStackTrace(); |
| | | // } |
| | | // |
| | | // } |
| | | @Override |
| | | public String payCallback(JSONObject jsonObject) { |
| | | String orderStatus = jsonObject.get("order_status").toString(); |
| | | if(2 != Integer.parseInt(orderStatus)){ |
| | | return "FAIL"; |
| | | } |
| | | |
| | | String orderNo = jsonObject.get("out_order_no").toString(); |
| | | |
| | | LambdaQueryWrapper<MallOrderInfo> query = new LambdaQueryWrapper<>(); |
| | | query.eq(MallOrderInfo::getOrderNo, orderNo); |
| | | MallOrderInfo orderInfo = this.orderInfoMapper.selectOne(query); |
| | | if ("1".equals(orderInfo.getPayResult())) { |
| | | return "SUCCESS"; |
| | | } |
| | | |
| | | orderInfo.setStatus(OrderStatusEnum.WAIT_SHIPPING.getValue()); |
| | | orderInfo.setPayResult("1"); |
| | | orderInfo.setPayTime(new Date()); |
| | | String payOrderNo = jsonObject.get("pay_order_no").toString(); |
| | | orderInfo.setPayTradeNo(payOrderNo); |
| | | orderInfoMapper.updateById(orderInfo); |
| | | |
| | | commonService.changeWallet(orderInfo.getId(), FlowTypeEnum.WECHAT.getValue()); |
| | | |
| | | /** |
| | | * 插入一条待处理记录 |
| | | * mq处理之后,更新状态 |
| | | */ |
| | | MallMqRecord mallMqRecord = new MallMqRecord(); |
| | | mallMqRecord.setOrderId(orderInfo.getId()); |
| | | mallMqRecord.setState(2); |
| | | mallMqRecord.setRetryTimes(2); |
| | | mallMqRecordMapper.insert(mallMqRecord); |
| | | //发送补贴消息 |
| | | agentProducer.sendPerkMoneyMsg(orderInfo.getId()); |
| | | //发送代理自动升级消息 |
| | | agentProducer.sendAutoLevelUpMsg(orderInfo.getMemberId()); |
| | | |
| | | return "SUCCESS"; |
| | | } |
| | | |
| | | public static void main(String[] args) { |
| | | |
| | | /** |
| | | * {"req_time":"20221026100323", |
| | | * "version":"1.0", |
| | | * "req_data":{ |
| | | * "out_order_no":"GHSNVDY8033038232443530", |
| | | * "merchant_no":"8222900701107M5", |
| | | * "total_amount":1, |
| | | * "order_efficient_time":"20221118220823", |
| | | * "notify_url":"", |
| | | * "support_cancel":0, |
| | | * "support_refund":1, |
| | | * "support_repeat_pay":1, |
| | | * "order_info":"测试"}} |
| | | */ |
| | | LaKaLaCreateOrderReqDate laKaLaCreateOrderReqDate = new LaKaLaCreateOrderReqDate(); |
| | | laKaLaCreateOrderReqDate.setOut_order_no("GHSNVDY8033038232443530"); |
| | | |
| | | LaKaLaBasicReqDate laKaLaBasicReqDate = new LaKaLaBasicReqDate(); |
| | | laKaLaBasicReqDate.setReq_time(DateUtil.format(new Date(),"yyyyMMddHHmmss")); |
| | | laKaLaBasicReqDate.setVersion("3.0"); |
| | | JSONObject jsonObject = JSONUtil.parseObj(laKaLaCreateOrderReqDate); |
| | | laKaLaBasicReqDate.setReq_data(jsonObject); |
| | | |
| | | String s = "{\n" + |
| | | " \"code\":\"000000\",\n" + |
| | | " \"msg\":\"操作成功\",\n" + |
| | | " \"resp_time\":\"20230419151705\",\n" + |
| | | " \"resp_data\":{\n" + |
| | | " \"merchant_no\":\"8222900701107M5\",\n" + |
| | | " \"channel_id\":\"95\",\n" + |
| | | " \"out_order_no\":\"HLM20230419151704\",\n" + |
| | | " \"order_create_time\":\"20230419151705\",\n" + |
| | | " \"order_efficient_time\":\"20230419161704\",\n" + |
| | | " \"pay_order_no\":\"23041911012001101011000965837\",\n" + |
| | | " \"total_amount\":\"1\",\n" + |
| | | " \"counter_url\":\"http://q.huijingcai.top/r/0000?pageStyle%3DV2%26token%3DCCSSIZlwnaqWmBw3FHY5U40nGcX7pxoKuxVhhVI7XyulHEUboR1I0VTIqJRMAjgj3IjCMWvoWjp9%2BXHpkA%3D%3D%26amount%3D1%26payOrderNo%3D23041911012001101011000965837\"\n" + |
| | | " }\n" + |
| | | "}"; |
| | | |
| | | JSONObject parseObj = JSONUtil.parseObj(s); |
| | | |
| | | Object respData = parseObj.get("resp_data"); |
| | | |
| | | JSONObject respDataChild = JSONUtil.parseObj(respData); |
| | | String counterUrl = respDataChild.get("counter_url").toString(); |
| | | System.out.println(counterUrl); |
| | | |
| | | } |
| | | |
| | | public HttpResponse post(String url, String message, String authorization) throws Exception { |
| | | SSLContext ctx = SSLContext.getInstance("TLS"); |
| | |
| | | String nonceStr = generateNonceStr(); |
| | | long timestamp = generateTimestamp(); |
| | | |
| | | String message = appid + "\n" + mchSerialNo + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; |
| | | |
| | | String appidStr = ""; |
| | | String mchSerialNoStr = ""; |
| | | if ("dev".equals(active) || "test".equals(active)) { |
| | | appidStr = appid; |
| | | mchSerialNoStr = mchSerialNo; |
| | | }else{ |
| | | appidStr = appid_prd; |
| | | mchSerialNoStr = mchSerialNo_prd; |
| | | } |
| | | |
| | | String message = appidStr + "\n" + mchSerialNoStr + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; |
| | | |
| | | System.out.println("getToken message : " + message); |
| | | |
| | | PrivateKey merchantPrivateKey = loadPrivateKey(new FileInputStream(new File(merchantPrivateKeyPath))); |
| | | String merchantPrivateKeyPathNameStr = ""; |
| | | if ("dev".equals(active) || "test".equals(active)) { |
| | | merchantPrivateKeyPathNameStr = merchantPrivateKeyPathName; |
| | | }else{ |
| | | merchantPrivateKeyPathNameStr = merchantPrivateKeyPathName_prd; |
| | | } |
| | | InputStream fileInputStream = resourceLoader.getResource(merchantPrivateKeyPathNameStr).getInputStream(); |
| | | PrivateKey merchantPrivateKey = loadPrivateKey(fileInputStream); |
| | | |
| | | String signature = this.sign(message.getBytes(ENCODING), merchantPrivateKey); |
| | | |
| | | String authorization = "appid=\"" + appid + "\"," + "serial_no=\"" + mchSerialNo + "\"," + "timestamp=\"" |
| | | String authorization = "appid=\"" + appidStr + "\"," + "serial_no=\"" + mchSerialNoStr + "\"," + "timestamp=\"" |
| | | + timestamp + "\"," + "nonce_str=\"" + nonceStr + "\"," + "signature=\"" + signature + "\""; |
| | | System.out.println("authorization message :" + authorization); |
| | | |