微信V3小程序支付&本地测试回调方法(NATAPP内网穿透)

微信文档:

微信支付开发者文档


pom依赖

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
</dependency>

需要配置

#微信支付
#应用appId  服务商模式下为服务商的appid 必填
wxpay.appId=@wxpay.appId@
#  v3 密钥 必填
wxpay.appSecret=@wxpay.appSecret@
#  微信支付商户号 服务商模式下为服务商的mchid 必填
wxpay.mchId=@wxpay.mchId@
#商户API私钥路径
wxpay.privateKeyPath=@wxpay.privateKeyPath@
#商户证书序列号
wxpay.merchantSerialNumber=@wxpay.merchantSerialNumber@
#微信支付的商户密钥(开发者在微信商户平台查询)
wxpay.apiV3key=@wxpay.apiV3key@
# 支付成功后的服务器回调url(后台随便的一个接口能接收到就行)
wxpay.notifyUrl=@wxpay.notifyUrl@
私钥文件

创建配置类

package com.blockchain.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
@Component
public class WechatConfig {
    @Value("${wxpay.appId}")
    private String appid;
    @Value("${wxpay.appSecret}")
    private String appSecret;
    @Value("${wxpay.mchId}")
    private String mchId;
    @Value("${wxpay.apiV3key}")
    private String apiV3key;
    @Value("${wxpay.notifyUrl}")
    private String notifyUrl;
    @Value("${wxpay.privateKeyPath}")
    private String privateKeyPath;
    @Value("${wxpay.merchantSerialNumber}")
    private String merchantSerialNumber;

    public String getAppid() {
        return appid;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public String getMchId() {
        return mchId;
    }

    public String getApiV3key() {
        return apiV3key;
    }

    public String getNotifyUrl() {
        return notifyUrl;
    }

    public String getPrivateKeyPath() {
        return privateKeyPath;
    }

    public String getMerchantSerialNumber() {
        return merchantSerialNumber;
    }

}

生成 jsapiService  refundService Bean

package com.blockchain.config;

import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.refund.RefundService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

/**
 * @Description:
 * @Author: kaysen
 * @Date: 2023/2/27 2:29 PM
 */
@Configuration
@Component
public class WechatPayConfig {
    /** 商户号 */
    @Value("${wxpay.mchId}")
    private  String merchantId;
    /** 商户API私钥路径 */
    @Value("${wxpay.privateKeyPath}")
    private  String privateKeyPath;
    /** 商户证书序列号 */
    @Value("${wxpay.merchantSerialNumber}")
    private  String merchantSerialNumber;
    /** 商户APIV3密钥 */
    @Value("${wxpay.apiV3key}")
    private  String apiV3key;

    public  RSAAutoCertificateConfig config;

    public  NotificationParser parser;

    public JsapiService service;

    public RefundService refundService;
    @Bean("jsapiService")
    public JsapiService getJsapiService() {
        if (ObjectUtils.isEmpty(config)){
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(privateKeyPath);
            String key = getStringByInputStream(resourceAsStream);
            config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(merchantId)
                    .privateKey(key)
                    .merchantSerialNumber(merchantSerialNumber)
                    .apiV3Key(apiV3key)
                    .build();
        }
        if (ObjectUtils.isEmpty(service)){
            service = new JsapiService.Builder().config(config).build();
        }
        return service;
    }

    @Bean("refundService")
    public RefundService getRefundService() {
        if (ObjectUtils.isEmpty(config)){
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(privateKeyPath);
            String key = getStringByInputStream(resourceAsStream);
            config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(merchantId)
                    .privateKey(key)
                    .merchantSerialNumber(merchantSerialNumber)
                    .apiV3Key(apiV3key)
                    .build();
        }
        if (ObjectUtils.isEmpty(refundService)){
            refundService = new RefundService.Builder().config(config).build();
        }
        return refundService;
    }
    
    //解密报文
    @Bean("notificationParser")
    public NotificationParser getNotificationConfig() {
        if (ObjectUtils.isEmpty(config)){
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(privateKeyPath);
            String key = getStringByInputStream(resourceAsStream);
            config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(merchantId)
                    .privateKey(key)
                    .merchantSerialNumber(merchantSerialNumber)
                    .apiV3Key(apiV3key)
                    .build();
        }
        if (ObjectUtils.isEmpty(parser)){
            parser = new NotificationParser(config);
        }
        return parser;
    }



    /**
     * 获取私钥。
     *
     * @return 私钥对象
     */
    @Bean("privateKey")
    public PrivateKey getPrivateKey() throws IOException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(privateKeyPath);
        String content = getStringByInputStream(resourceAsStream);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }


    public static String getStringByInputStream(InputStream inputStream){
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                result.append(line);
            }
            return result.toString();
        } catch (Exception e) {
            try {
                inputStream.close();
                bufferedReader.close();
            } catch (Exception e1) {
            }
        }
        return null;
    }
}

创建工具类

生成订单编号 关闭订单 查询订单 申请退款 查看单笔退款 支付成功回调方法等工具类

package com.blockchain.Utils;

import com.blockchain.config.WechatConfig;
import com.blockchain.controller.base.BaseController;
import com.blockchain.model.WxPayDto;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;


/**
 * @BelongsProject: ouya_backend
 * @BelongsPackage: com.blockchain.Utils
 * @Author: KaySen
 * @CreateTime: 2023-03-02  15:09
 * @Description: TODO
 * @Version: 1.0
 */
@Component
public class WechatPayUtils {
    private static Logger log = LoggerFactory.getLogger(BaseController.class);
    private final static String algorithm = "SHA256withRSA";
    @Autowired
    private WechatConfig wechatConfig;

    @Resource(name = "jsapiService")
    private JsapiService jsapiService;//用于支付 查询订单 关闭订单
    @Resource
    private RefundService refundService;//用于退款创建 查询

    @Resource(name = "notificationParser")
    private NotificationParser notificationParser;
    @Autowired
    private WxSignUtil wxSignUtil;

    //生成微信付款编号
    public String wechatPay(WxPayDto dto) throws Exception {
        PrepayRequest prepayRequest = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal((int) (dto.getMoney() * 100));//单位分
        prepayRequest.setAmount(amount);
        prepayRequest.setAppid(wechatConfig.getAppid());
        prepayRequest.setMchid(wechatConfig.getMchId());
        prepayRequest.setDescription(dto.getDescription());
        prepayRequest.setNotifyUrl(wechatConfig.getNotifyUrl());
        prepayRequest.setOutTradeNo(dto.getOrderReference());
        Payer payer = new Payer();
        payer.setOpenid(dto.getOpenId());
        prepayRequest.setPayer(payer);
        PrepayResponse response = jsapiService.prepay(prepayRequest);
        //System.out.println("微信支付:编号"+response.getPrepayId());
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(wechatConfig.getPrivateKeyPath());
        String key = getStringByInputStream(resourceAsStream);
        String json = wxSignUtil.WxAppPayTuneUp(response.getPrepayId(), wechatConfig.getAppid(), wechatConfig.getMchId(), key, dto.getOrderReference());
        return json;
    }

    //根据微信订单编号查询订单状态
    public Transaction queryOrderById(String orderReference) throws Exception {
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(wechatConfig.getMchId());
        queryRequest.setOutTradeNo(orderReference);
        try {
            Transaction result = jsapiService.queryOrderByOutTradeNo(queryRequest);
            return result;
        } catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
            return null;
        }

    }

    //关闭订单
    public String closeOrderRequest(String orderSn) throws Exception {
        CloseOrderRequest closeRequest = new CloseOrderRequest();
        closeRequest.setMchid(wechatConfig.getMchId());
        closeRequest.setOutTradeNo(orderSn);
        try {
            jsapiService.closeOrder(closeRequest);
        } catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
            return e.getErrorMessage();
        }
        return null;
    }

    //退款申请 思路
    public String refund(){
        CreateRequest createRequest=new CreateRequest();
        // 商户订单号
        // 商户退款单号
        // 订单总金额(单位为分)
        // 退款金额(单位为分)
        // 退款原因
        // 退款结果回调url
        //......

        // createRequest.setXxx(val)设置所需参数,具体参数可见Request定义
        //以下是CreateRequest 实体类里的参数
//        /** 子商户号 说明:子商户的商户号,由微信支付生成并下发。服务商模式下必须传递此参数 */
//        @SerializedName("sub_mchid")
//        private String subMchid;
//        /** 微信支付订单号 说明:原支付交易对应的微信订单号 */
//        @SerializedName("transaction_id")
//        private String transactionId;
//        /** 商户订单号 说明:原支付交易对应的商户订单号 */
//        @SerializedName("out_trade_no")
//        private String outTradeNo;
//        /** 商户退款单号 说明:商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 */
//        @SerializedName("out_refund_no")
//        private String outRefundNo;
//        /** 退款原因 说明:若商户传入,会在下发给用户的退款消息中体现退款原因 */
//        @SerializedName("reason")
//        private String reason;
//        /**
//         * 退款结果回调url 说明:异步接收微信支付退款结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
//         * 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效,优先回调当前传的这个地址。
//         */
//        @SerializedName("notify_url")
//        private String notifyUrl;
//        /** 退款商品 说明:指定商品退款需要传此参数,其他场景无需传递 */
//        @SerializedName("goods_detail")
//        private List<com.wechat.pay.java.service.refund.model.GoodsDetail> goodsDetail;
//        /** 金额信息 说明:订单金额信息 */
//        @SerializedName("amount")
//        private AmountReq amount;
//        /** 退款资金来源 说明:若传递此参数则使用对应的资金账户退款,否则默认使用未结算资金退款(仅对老资金流商户适用) 枚举值: - AVAILABLE:可用余额账户 */
//        @SerializedName("funds_account")
//        private ReqFundsAccount fundsAccount;

        // 调用接口
        Refund refund = refundService.create(createRequest);
        return null;

    }

    //查询单笔退款(通过商户退款单号) 思路
    public String queryByOutRefundNo(){
        QueryByOutRefundNoRequest queryByOutRefundNoRequest=new QueryByOutRefundNoRequest();
        // createRequest.setXxx(val)设置所需参数,具体参数可见Request定义
//        * 商户退款单号 说明:商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
//        @SerializedName("out_refund_no")
//        @Expose(serialize = false)
//        private String outRefundNo;
//        * 子商户号 说明:子商户的商户号,由微信支付生成并下发。服务商模式下必须传递此参数
//        @SerializedName("sub_mchid")
//        @Expose(serialize = false)
//        private String subMchid;

        // 调用接口
        Refund refund = refundService.queryByOutRefundNo(queryByOutRefundNoRequest);
        return null;
    }

    //回调
    public Map<String, String> callBack(HttpServletRequest request) throws Exception {
        //获取报文
        String body = getRequestBody(request);
        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");
        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");
        //证书序列号(微信平台)
        String serialNo = request.getHeader("Wechatpay-Serial");
        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        //加密类型
        String signatureType = request.getHeader("Wechatpay-Signature-Type");

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serialNo)
                .nonce(nonceStr)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signatureType)// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
                .body(body)
                .build();

        Transaction transaction = notificationParser.parse(requestParam, Transaction.class);
        //处理业务逻辑 TODO
        Map<String, String> map = new HashMap<>(2);
        Transaction.TradeStateEnum tradeState = transaction.getTradeState();
        if (tradeState.SUCCESS.name().equals("SUCCESS")) {
            //订单编号(本地生成)
            String outTradeNo = transaction.getOutTradeNo();
            //付款编号 (微信返回)
            String transactionId = transaction.getTransactionId();
            //通过本地订单编号处理业务逻辑(并将付款编号保存到本地)

            map.put("code", "200");
            return map;
        }

        //响应微信
        map.put("code", "FAIL");
        map.put("message", "没有该订单");
        return map;
    }


    /**
     * 读取请求数据流
     *
     * @param request
     * @return
     */
    private String getRequestBody(HttpServletRequest request) {

        StringBuffer sb = new StringBuffer();

        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            log.error("读取数据流异常:{}", e);
        }
        return sb.toString();
    }

    public String getStringByInputStream(InputStream inputStream) {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                result.append(line);
            }
            return result.toString();
        } catch (Exception e) {
            try {
                inputStream.close();
                bufferedReader.close();
            } catch (Exception e1) {
            }
        }
        return null;
    }
}

微信调起支付工具类

package com.blockchain.Utils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.wechat.pay.java.core.util.PemUtil;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.security.*;
import java.util.Base64;

@Component
public class WxSignUtil {

    protected  final SecureRandom RANDOM = new SecureRandom();

    /**
     * 微信调起支付参数
     * 返回参数如有不理解 请访问微信官方文档
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml
     *
     * @param prepayId         微信下单返回的prepay_id
     * @param appId            应用ID
     * @param mch_id           商户号
     * @param privateKey       私钥
     * @return 当前调起支付所需的参数
     * @throws Exception
     */
    public  String WxAppPayTuneUp(String prepayId, String appId, String mch_id, String privateKey,String outTradeNo) throws Exception {
        if (StringUtils.isNotBlank(prepayId)) {
            long timestamp = System.currentTimeMillis() / 1000;
            String nonceStr = generateNonceStr();
            //加载签名
            String packageSign = sign(buildMessage(appId, timestamp, nonceStr, prepayId).getBytes(), privateKey);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("appId", appId);
            jsonObject.put("prepayId", prepayId);
            jsonObject.put("timeStamp", timestamp);
            jsonObject.put("nonceStr", nonceStr);
            jsonObject.put("package", "Sign=WXPay");
            jsonObject.put("signType", "RSA");
            jsonObject.put("sign", packageSign);
            jsonObject.put("partnerId", mch_id);
            jsonObject.put("outTradeNo", outTradeNo);
            return jsonObject.toJSONString();
        }
        return "";
    }

    public  String sign(byte[] message, String privateKey) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
        //签名方式
        Signature sign = Signature.getInstance("SHA256withRSA");
        //私钥
        sign.initSign(PemUtil.loadPrivateKeyFromString(privateKey));
        sign.update(message);

        return Base64.getEncoder().encodeToString(sign.sign());
    }

    //生成随机字符串 微信底层的方法,直接copy出来了
    protected  String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(RANDOM.nextInt("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".length()));
        }
        return new String(nonceChars);
    }

    /**
     * 按照前端签名文档规范进行排序,\n是换行
     *
     * @param appId     appId
     * @param timestamp 时间
     * @param nonceStr  随机字符串
     * @param prepay_id prepay_id
     * @return
     */
    public  String buildMessage(String appId, long timestamp, String nonceStr, String prepay_id) {
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + "prepay_id="+prepay_id + "\n";
    }

}



生成微信付款编号DTO

package com.blockchain.model;

import lombok.*;

import java.io.Serializable;

/**
 * @BelongsProject: ouya_backend
 * @BelongsPackage: com.blockchain.model
 * @Author: KaySen
 * @CreateTime: 2023-02-22  16:41
 * @Description: 微信支付实体类
 * @Version: 1.0
 */
@Data
public class WxPayDto implements Serializable {
    private Double money;   //缴费金额
    private String description; //商品名字
    private String orderReference; //订单编号
    private String openId; //支付人的openid

}

生成订单编号工具

package com.blockchain.utils;

import com.blockchain.util.UUIDUtil;

import java.text.SimpleDateFormat;
import java.util.Date;

public class OrderNoUtils {

    /**
     * @Description <p> 生成订单编号 </P>
     * @param type 充值CZ
     * @param payType 支付宝zf;微信wx
     * @param userId 用户id
     */
    public static String copyPropertiesIgnoreNull(String type, Object payType,Integer userId) {
        String orderNo=type+userId+payType;
        String uuid = UUIDUtil.getUUID(5);
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMddHHmmss");
        Date date = new Date();
        String format = simpleDateFormat.format(date);
        orderNo=orderNo+format+uuid;
        return orderNo;
    }


}


Controller层:

@Api(tags = "微信充值")
@RestController
@RequestMapping(value = "/weiXinPay")
public class WechatPayController extends BaseController {
//先本地生成了订单 通过本地订单编号查询到生成的订单 在生成微信付款Json参数    
@ApiOperation(value = "创建微信付款订单", notes = "创建微信付款订单")
    @ResponseBody
    @RequestMapping(value = "/wxPay", method = RequestMethod.GET)
    public MessageResult wxPay(@SessionAttribute(SESSION_MEMBER) AuthMember user,
                                     @ApiParam("订单编号")@RequestParam(name = "orderSn")String orderSn) {
        CdMember member = cdMemberService.getById(user.getUserId());
        if (ObjectUtils.isEmpty(member)){
            return error("没有该用户");
        }
        if (ObjectUtils.isEmpty(member.getOpenId())){
            return error("没有该用户openId为null");
        }
        CdOrder cdOrder = cdOrderService.getByOrderSn(user.getUserId(), orderSn);
        if (ObjectUtils.isEmpty(cdOrder)){
            return error("该用户没有该订单");
        }
        WxPayDto wxPayDto=new WxPayDto();
        wxPayDto.setMoney(cdOrder.getMoney().doubleValue());
        wxPayDto.setDescription("充值");
        wxPayDto.setOrderReference(cdOrder.getOrderSn());//本地生成的订单编号
        wxPayDto.setOpenId(member.getOpenId());
        String json = null;
        try {
            json = wechatPayUtils.wechatPay(wxPayDto);
        } catch (Exception e) {
            return error("创建微信订单失败");
        }
        cdOrder.setWxJson(json);
        cdOrderService.updateById(cdOrder);
        return success(json);
    } 

 @ApiOperation(value = "取消充值订单", notes = "取消充值订单")
    @ResponseBody
    @RequestMapping(value = "/offOrder", method = RequestMethod.GET)
    public MessageResult offOrder(@SessionAttribute(SESSION_MEMBER) AuthMember user,
                                  @ApiParam("充值订单id") @RequestParam(name = "orderId") Integer orderId) {
        CdOrder cdOrder = null;
        try {
            cdOrder = cdOrderService.offOrder(user.getUserId(), orderId);
            try {
                String result = wechatPayUtils.closeOrderRequest(cdOrder.getOrderSn());//本地生成的订单编号
            } catch (Exception e) {
                e.printStackTrace();
            }
            cdOrder.setWxJson("已取消订单");
            cdOrderService.updateById(cdOrder);
        } catch (BusinessException e) {
            return error(e.getErrorCode(), e.getErrorMessage());
        }
        return success("取消成功");
    }


@RequestMapping(value = "/callBack", method = RequestMethod.POST)
    @ResponseBody
    @ApiOperation(value = "回调接口", position = 2, notes = "回调接口")
    public String callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> map = wechatPayUtils.callBack(request);
        String result = JSON.toJSONString(map);
        if (map.get("code").equals("200")){
            response.setStatus(200);
            return null;
        }
        if (map.get("code").equals("FAIL")){
            response.setStatus(400);
            return result;
        }
        response.setStatus(500);
        Map<String, String> errorMap = new HashMap<>();
        errorMap.put("code", "FAIL");
        errorMap.put("message", "服务器内部错误");
        String s = JSON.toJSONString(errorMap);
        return s;
    }
}

本地测试回调方法:NATAPP使用教程(内网穿透)

在开发时可能会有将自己开发的机器上的应用提供到公网上进行访问,但是并不想通过注册域名、搭建服务器;由此可以使用natapp(内网穿透)

1. 注册用户

官网:https://natapp.cn

2. 购买免费隧道

3. 修改隧道配置

4. 建立一个web项目

访问 localhost:8080/index.jsp

5. 下载对应的客户端

  • 下载好后解压,得到natapp.exe

6. 运行natapp

通过config.ini的方式运行,放在natapp的同级目录下

  • 参考网址:https://natapp.cn/article/config_ini
#将本文件放置于natapp同级目录 程序将读取 [default] 段
#在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
#命令行参数 -config= 可以指定任意config.ini文件
[default]
authtoken=取得authtoken(刚刚才购买的隧道)                    #对应一条隧道的authtoken
clienttoken=                    #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none                        #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=ERROR                  #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy=                  #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空

7. 测试运行

双击natapp.exe

7.1. 浏览器访问

访问 http://6dnnar.natappfree.cc/index.jsp

这个网址可以任意人访问!!!

8 设置微信支付成功回调url

# 支付成功后的服务器回调url(后台随便的一个接口能接收到就行) wxpay.notifyUrl=http://6dnnar.natappfree.cc/weiXinPay/callBack

注:vue项目

vue项目应该在devServer下添加disableHostCheck: true,跳过检查;

否则会报Invalid Host header

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值