微信,native下单功能

表现层

package com.fy.staffapi.app.controller;


import com.fy.common.protocol.JsonResult;
import com.fy.staffapi.service.ITransactionsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * 微信交易
 */
@Slf4j
@RequestMapping("transactions")
@RestController
public class TransactionsController {

    @Autowired
    private ITransactionsService transactionsService;

    /**
     * 生成收费链接
     * @param money 金额 以元为单位
     * @param description 描述
     * @param outTradeNo 订单号
     * @return 收费链接
     */
    @GetMapping("generatePaymentQRCode")
    public JsonResult generatePaymentQrCode(String money, String description, String outTradeNo) {
        try {
            String data = transactionsService.generatePaymentQrCode(money, description, outTradeNo);
            return JsonResult.success().data(data).build();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("微信收费链接生成失败,IO异常:{}", e.getMessage());
            return JsonResult.failure().build();
        }
    }

}

事务层

package com.fy.staffapi.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fy.common.okhttp.OkHttp;
import com.fy.staffapi.service.ITransactionsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class ITransactionsServiceImpl implements ITransactionsService {

    /**
     * Native下单API 请求URL
     */
    @Value("${wechat.collection.transactions-url}")
    private String transactionsUrl;

    /**
     * 应用ID
     */
    @Value("${wechat.collection.appid}")
    private String appid;

    /**
     * 直连商户号
     */
    @Value("${wechat.collection.mchid}")
    private String mchid;


    /**
     * 证书序列号
     */
    @Value("${wechat.collection.serial-no}")
    private String serialNo;

    /**
     * 回调接口路径
     */
    @Value("${wechat.collection.call-back-url}")
    private String callBackUrl;

    /**
     * 密钥
     */
    @Value("${wechat.cert-file.pwd}")
    private String key;

    /**
     * 密钥
     */
    @Value("${wechat.cert-file.file-name}")
    private String fileName;

    /**
     * 网络请求工具类
     */
    @Autowired
    @Qualifier("encryptionOkHttp")
    private OkHttp encryptionOkHttp;

    /**
     * json转换工具类
     */
    @Autowired
    private ObjectMapper objectMapper;

    @Value("${wechat.cert-file.file-name:}")
    private String certFileName;
 
    /**
     * 生成收款二维码
     *
     * @param money 金额
     * @return 二维码链接
     */
    @Override
    public String generatePaymentQrCode(String money, String description, String outTradeNo) throws IOException {
        ObjectNode rootNode = objectMapper.createObjectNode();
        // 直连商户号
        rootNode.put("mchid", mchid)
                // 应用ID
                .put("appid", appid)
                // 商品描述
                .put("description", description)
                // 通知地址
                .put("notify_url", callBackUrl)
                // 商户订单号
                .put("out_trade_no", outTradeNo);
        // 订单金额 把元改成以分为单位
        BigDecimal multiply = new BigDecimal(money).multiply(new BigDecimal(100));
        rootNode.putObject("amount")
                // 总金额
                .put("total", multiply.intValue()).put("currency", "CNY");
        String jsonParam = objectMapper.writeValueAsString(rootNode);
        // 头文件
        Map<String, String> header = initHeader(mchid, jsonParam);
        String result = encryptionOkHttp.postJson(transactionsUrl, jsonParam, header);
        if (!result.contains("code_url")) {
            log.error("微信收费链接生成失败,记录日志:{}", result);
        }
        return result;
    }

    /**
     * 生成头文件,请求形式,签名等信息
     *
     * @param mchid 商户号
     * @param json  post请求body体里的数据
     * @return
     */
    private Map<String, String> initHeader(String mchid, String json) {
        // 当前毫秒值
        long now = System.currentTimeMillis() / 1000;
        // 32位随机数
        String random = RandomStringUtils.randomAlphanumeric(32);
        // 返回值初始化
        Map<String, String> header = new HashMap<>(16);
        header.put("Accept", "application/json");
        header.put("Content-type", "application/json; charset=utf-8");
        // 签名
        String sign = createSign(now, json, random);
        String append = "WECHATPAY2-SHA256-RSA2048 " +
                "mchid=\"" + mchid + "\","
                + "nonce_str=\"" + random + "\","
                + "signature=\"" + sign + "\","
                + "timestamp=\"" + now + "\","
                + "serial_no=\"" + serialNo + "\"";
        header.put("Authorization", append);
        return header;
    }

    /**
     * 生成sign
     *
     * @param now       当前毫秒值
     * @param jsonParam 请求数据
     * @param random    随机数
     * @return 签名
     */
    private String createSign(long now, String jsonParam, String random) {
        String str = // 请求方式
                "POST" + "\n" +
                        // 去掉域名的方法路径
                        transactionsUrl.substring(transactionsUrl.indexOf(".com") + 4) + "\n" +
                        // 系统当前时间戳
                        now + "\n" +
                        // 生成一个请求随机串
                        random + "\n" +
                        jsonParam + "\n";
        return sign(str, key);
    }

    /**
     * 签名 加密
     *
     * @param data          加密数据
     * @param privateKeyPwd Key
     * @return 加密后的签名
     */
    private String sign(String data, String privateKeyPwd) {
        if (data == null || privateKeyPwd == null) {
            return "";
        }
        try {
            // 获取证书的私钥
            PrivateKey key = readPrivate(privateKeyPwd);
            // 进行签名服务
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(key);
            signature.update(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("创建签名实例失败,加密算法异常:{}", e.getMessage());
        } catch (SignatureException | InvalidKeyException e) {
            e.printStackTrace();
            log.error("创建签名实例失败,签名生成异常:{}", e.getMessage());
        }
        return "";
    }

    /**
     * 读取私钥
     *
     * @param privateKeyPwd 密钥
     * @return 证书
     */
    private PrivateKey readPrivate(String privateKeyPwd) {
        if (privateKeyPwd == null) {
            return null;
        }
        try {
            // 获取JKS 服务器私有证书的私钥,取得标准的JKS的 KeyStore实例
            KeyStore store = KeyStore.getInstance("JKS");
            ClassPathResource cl = new ClassPathResource(fileName);
            // jks文件密码,根据实际情况修改
            store.load(cl.getInputStream(), privateKeyPwd.toCharArray());
            // 获取jks证书别名
            Enumeration<String> en = store.aliases();
            String pName = null;
            while (en.hasMoreElements()) {
                String n = en.nextElement();
                if (store.isKeyEntry(n)) {
                    pName = n;
                }
            }
            // 获取证书的私钥
            return (PrivateKey) store.getKey(pName,
                    privateKeyPwd.toCharArray());
        } catch (IOException e) {
            e.printStackTrace();
            log.error("读取证书文件失败,IO异常:{}", e.getMessage());
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
            log.error("读取证书文件失败,打开证书的Key不正确:{}", e.getMessage());
        } catch (KeyStoreException e) {
            e.printStackTrace();
            log.error("读取证书内容失败,证书打开异常:{}", e.getMessage());
        } catch (CertificateException e) {
            e.printStackTrace();
            log.error("读取证书文件失败,证书异常:{}", e.getMessage());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            log.error("读取证书文件失败,算法异常:{}", e.getMessage());
        }
        return null;
    }



    /**
     * 实例化encryptionOkHttp 生成
     * 含证书请求
     *
     * @return
     */
    @Bean("encryptionOkHttp")
    public OkHttp encryptionOkHttp() throws Exception {
        OkHttp okHttp = new OkHttp();
        ClassPathResource cl = new ClassPathResource(certFileName);
        InputStream inputStream = cl.getInputStream();
        byte[] bytes = IOUtils.toByteArray(inputStream);
        okHttp.buildWithCert(bytes, mchid);
        return okHttp;
    }

}

在这里插入图片描述
需要把证书放到resource下,其他还有很多需要用的比如商户信息等功能,需要到微信端查询

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值