微信小程序中实现微信支付

时间:2024-01-23 10:50:32

最近在做微信小程序,今天刚好做到小程序里的微信支付这块,踩过不少坑,特此写个博客记录下,希望能帮到其它人吧。

我总结了一下,小程序中的微信支付和之前其它的公众号里的微信支付有两个区别,第一就是小程序必须要Https接口,第二服务器在返回给小程序之前需要二次签名,记住要二次签名。

我发现网上的相关例子很多,但是也很乱,各种封装类,五花八门的,在这里我用的是微信官方的方法,简单,无脑,高效。

1,我们需要在Maven坐标系里导入官方封装好的方法

<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>


2,写一个MyConfig类继承WXPayConfig
import com.baibeiyun.robot.base.util.PropertiesUtils;
import com.github.wxpay.sdk.WXPayConfig;

import java.io.InputStream;

/**
 * 配置类
 *
 * @author liyuan
 * @date 2018/3/30
 **/
public class MyConfig implements WXPayConfig {

    @Override
    public String getAppID() {
     // 放你的appid
return PropertiesUtils.getInstace("conf/webService.properties").getProperty("appId"); } @Override public String getMchID() {
     // 商户号
return PropertiesUtils.getInstace("conf/webService.properties").getProperty("mchId"); } @Override public String getKey() {
     // 你的商户号里的key
return PropertiesUtils.getInstace("conf/webService.properties").getProperty("key"); } @Override public InputStream getCertStream() { return null; } @Override public int getHttpConnectTimeoutMs() { return 8000; } @Override public int getHttpReadTimeoutMs() { return 10000; } }

3. 在服务器端充值接口里获取到那5个关键参数传给小程序

    @ApiOperation("小程序充值接口")
    @RequestMapping(value = "saveMoney", method = RequestMethod.POST)
    public String saveMoney(@ModelAttribute SaveMoney1Dto dto, HttpServletRequest request) throws Exception {
        if (!dto.checkParam()) {
            throw new MyParameterException(400, "请求参数异常");
        }

        // 获取用户userId
        Integer userId = Integer.valueOf(redisService.get(dto.getUuidKey()).substring(52));
        // 生成交易订单号
        String orderNum = CommonUtils.getOrderNumber();
        MoneyTransaction moneyTransaction = new MoneyTransaction();
        moneyTransaction.setPayUserId(userId);
        moneyTransaction.setOrderNum(orderNum);
        moneyTransaction.setMoney(dto.getMoney());
        // 交易时间
        moneyTransaction.setGmtCreate(new Date());
        // 将交易订单存入数据库
        iMoneyTransactionService.insertSelective(moneyTransaction);


        // 给微信服务器统一下单
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);
        // 封装入参字典
        Map<String, String> data = new HashMap<>(16);
        // 商品描述
        data.put("body", "机器人服务充值");
        // 商户订单号
        data.put("out_trade_no", orderNum);
        // 随机字符串
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        // 总金额 分 将BigDecimal乘以100再转为字符串
        String totalFee = (dto.getMoney().multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP)).toString();
        data.put("total_fee", totalFee);
        // 终端IP
        data.put("spbill_create_ip", request.getRemoteAddr());
        // 接收微信支付异步通知回调地址
        data.put("notify_url", PropertiesUtils.getInstace("conf/webService.properties").getProperty("notifyUrl"));
        // 交易类型
        data.put("trade_type", "JSAPI");
        // 获取opneid
        String openid = redisService.get(dto.getUuidKey()).substring(24, 52);
        data.put("openid", openid);


        // 和微信服务器交互,统一下单
        Map<String, String> resp = wxpay.unifiedOrder(data);
        log.info("微信返回: " + resp);

        // 小程序再次签名
        Map<String, String> data2 = new HashMap<>(16);
        data2.put("appId", resp.get("appid"));
        String timeStamp = System.currentTimeMillis()/1000 + "";
        data2.put("timeStamp", timeStamp);
        String nonceStr =  WXPayUtil.generateNonceStr();
        data2.put("nonceStr", nonceStr);
        String packages = "prepay_id="+resp.get("prepay_id");
        data2.put("package", packages);
        data2.put("signType", "MD5");
        String sign2 = WXPayUtil.generateSignature(data2, PropertiesUtils.getInstace("conf/webService.properties").getProperty("key"));

        // 封装返回给前端map
        Map<String, String> result = new HashMap<>(16);
        result.put("timeStamp", timeStamp);
        result.put("nonceStr", nonceStr);
        result.put("package", packages);
        result.put("signType", "MD5");
        result.put("paySign", sign2);

        log.info("返回给前端: " + result);
        return YuanResultUtil.successWithDataAndMsg(result, "OK");
    }

在这里直接用微信的工具类,微信的方法比较高效,直接用map字典比较直观,犯不上像网上大多数人那样造一个封装类转来转去的麻烦

4 微信通知回调类

    @ApiOperation("小程序充值结果(自动从微信服务器获取,前端用不到)")
    @RequestMapping(value = "saveMoneyResult", method = RequestMethod.POST)
    public String saveMoneyResult(HttpServletRequest request) throws Exception {
        // 获取支付结果,XML格式
        String notifyData = StreamUtil.read(request.getInputStream());
        log.info("支付结果: " + notifyData);

        MyConfig config = new MyConfig();
        WXPay wxPay = new WXPay(config);
        // 支付结果转成map
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
        if (wxPay.isPayResultNotifySignatureValid(notifyMap)) {
            // 签名正确
            log.info("签名正确: " + notifyMap);
            // 获取订单号
            String orderNum = notifyMap.get("out_trade_no");
            log.info("订单号:" + orderNum);
            // 获取金额(分)
            String  totalFee = notifyMap.get("total_fee");
            // 转为BigDecimal
            BigDecimal totalFee2 = new BigDecimal(totalFee);
            totalFee2 = totalFee2.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);

            // 根据订单号获取交易记录
            MoneyTransaction moneyTransaction = iMoneyTransactionService.getMoneyTransactionByOrderNum(orderNum);
            if (moneyTransaction != null && moneyTransaction.getStatus() == 0 && moneyTransaction.getMoney().equals(totalFee2)) {
                // 设置状态为支付成功
                moneyTransaction.setStatus(1);
                moneyTransaction.setGmtCreate(new Date());
                log.info("修改成功");
                iMoneyTransactionService.updateByPrimaryKeySelective(moneyTransaction);

                return "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
            } else {
                log.info("修改失败");
            }
        } else {
            // 签名错误
            throw new BuinessException(501, "签名错误");
        }
        return "<xml><return_code>FALL</return_code><return_msg>失败</return_msg></xml>";
    }