【微信支付】java-微信小程序支付-V3接口

这篇具有很好参考价值的文章主要介绍了【微信支付】java-微信小程序支付-V3接口。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、对接前准备

最开始需要在微信支付的官网注册一个商户;
在管理页面中申请关联小程序,通过小程序的 appid 进行关联;商户号和appid之间是多对多的关系
进入微信公众平台,功能-微信支付中确认关联

具体流程请浏览官方文档:接入前准备-小程序支付 | 微信支付商户平台文档中心

流程走完之后,需要获取以下参数:

1,商户 id: mchId,

2,小程序id:appId
3,商户证书序列号: 这个商户证书序列号在申请完证书之后就可以看到
4, 商户APIV3密钥, 我对接的是v3 接口 所以用APIV3密钥

当你按照文档下载商户证书zip,解压得到4个文件,一定要保存好。不能泄露

java 微信支付v3,微信,微信小程序,小程序

二、开始写代码

java 微信支付v3,微信,微信小程序,小程序

1.pom引入微信库

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.7</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.80</version>
</dependency>

2.yml文件写入配置

wxpay:
  #应用编号
  appId: xxxxxxxxxxxxx

  #商户号
  mchId: xxxxxxxxxxxxx

  # APIv3密钥
  apiV3Key: xxxxxxxxxxxxx

  # 支付通知回调, 本地测试内网穿透地址
  notifyUrl: http://405c3382p5.goho.co:25325/wenbo-pay/notify/payNotify

  # 退款通知回调,  本地测试内网穿透地址
  refundNotifyUrl: http://405c3382p5.goho.co:25325/wenbo-pay/notify/refundNotify

  # 密钥路径,resources根目录下
  keyPemPath: apiclient_key.pem

  # 商户证书序列号
  serialNo: xxxxxxxxxxxxx

  # 小程序密钥
  appSecret: xxxxxxxxxxxxx

3.商户API私钥 放入resources根目录下  能获取到就行

java 微信支付v3,微信,微信小程序,小程序

编写配置类获取yml配置

package com.example.pay.config;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * @ClassName: WechatPayConfig
 * @author: tang
 * @createTime 2023-08-10
 * 小程序支付配置类
 */
@Component
@Data
@Slf4j
@ConfigurationProperties(prefix = "wxpay")
public class WechatPayConfig {

    /**
     * 应用编号
     */
    private String appId;
    /**
     * 商户号
     */
    private String mchId;

    /**
     * APIv3密钥
     */
    private String apiV3Key;
    /**
     * 支付通知回调地址
     */
    private String notifyUrl;
    /**
     * 退款回调地址
     */
    private String refundNotifyUrl;

    /**
     * API 证书中的 key.pem
     */
    private String keyPemPath;

    /**
     * 商户序列号
     */
    private String serialNo;


    /**
     * 获取商户的私钥文件
     *
     * @param keyPemPath
     * @return
     */
    public PrivateKey getPrivateKey(String keyPemPath) {

        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath);
        if (inputStream == null) {
            throw new RuntimeException("私钥文件不存在");
        }
        return PemUtil.loadPrivateKey(inputStream);
    }

    /**
     * 获取证书管理器实例
     *
     * @return
     */
    @Bean
    public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException {

        log.info("获取证书管理器实例");

        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(keyPemPath);

        //私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey);

        //身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);

        // 使用定时更新的签名验证器,不需要传入证书
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));

        return certificatesManager.getVerifier(mchId);
    }


    /**
     * 获取支付http请求对象
     *
     * @param verifier
     * @return
     */
    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(Verifier verifier) {

        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(keyPemPath);

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, serialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        return builder.build();
    }
    
}

封装 支付-退款 请求API

package com.example.pay.config;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @ClassName: WechatPayRequest
 * @author: tang
 * @createTime 2023-08-10
 * 封装公共的请求API,用于在业务请求时的统一处理。
 */
@Component
@Slf4j
public class WechatPayRequest {


    @Resource
    private CloseableHttpClient wxPayClient;

    /**
     * 支付请求
     *
     * @param url
     * @param paramsStr
     * @return
     */
    public String wechatHttpOrderPost(String url, String paramsStr) {
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(paramsStr, "utf-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            httpPost.setHeader("Accept", "application/json");

            CloseableHttpResponse response = wxPayClient.execute(httpPost);
            //响应体
            HttpEntity entity = response.getEntity();
            String body = entity == null ? "" : EntityUtils.toString(entity);
            //响应状态码
            int statusCode = response.getStatusLine().getStatusCode();
            //处理成功,204是,关闭订单时微信返回的正常状态码
            if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
                log.info("成功, 返回结果 = " + body);
            } else {
                String msg = "微信支付请求失败,响应码 = " + statusCode + ",返回结果 = " + body;
                log.info("支付模块-生成订单 = " + msg);
                throw new RuntimeException(msg);
            }
            return body;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 退款请求
     *
     * @param url
     * @param paramsStr
     * @return
     */
    public String wechatHttpPost(String url, String paramsStr) {
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(paramsStr, "utf-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            httpPost.setHeader("Accept", "application/json");

            CloseableHttpResponse response = wxPayClient.execute(httpPost);
            //响应体
            HttpEntity entity = response.getEntity();
            String body = entity == null ? "" : EntityUtils.toString(entity);
            //响应状态码
            int statusCode = response.getStatusLine().getStatusCode();

            //处理成功,204是,关闭订单时微信返回的正常状态码
            if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
                log.info("成功, 返回结果 = " + body);
                // 请求成功或已处理成功,返回成功的响应
                return "退款处理中";
            } else if (statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
                // 请求参数错误或系统错误,返回失败的响应
                JSONObject json = JSONObject.parseObject(body);
                return json.getString("message");
            } else if (statusCode == HttpStatus.SC_FORBIDDEN) {
                // 权限问题,没有退款权限
                return "没有退款权限";
            } else if (statusCode == HttpStatus.SC_NOT_FOUND) {
                // 订单号不存在
                return "订单号不存在";
            } else if (statusCode == 429) {
                // 频率限制
                return "退款请求频率过高,请稍后重试";
            } else if (statusCode == HttpStatus.SC_PAYMENT_REQUIRED) {
                // 余额不足
                return "商户余额不足,请充值后重试";
            } else {
                // 其他状态码,返回通用的失败响应
                return "退款失败,请稍后重试";
            }
        } catch (Exception e) {
            log.info("支付模块-退款失败 = " + e.getMessage());
            JSONObject json = JSONObject.parseObject(e.getMessage());
            return json.getString("message");
        }
    }

}

支付回调解密处理

package com.example.pay.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;

import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;

/**
 * @ClassName: WechatPayValidator
 * @author: tang
 * @createTime 2023-08-10
 * 回调校验器
 * 用于对微信支付成功后的回调数据进行签名验证,保证数据的安全性与真实性。
 */
@Slf4j
public class WechatPayValidator {

    /**
     * 应答超时时间,单位为分钟
     */
    private static final long RESPONSE_EXPIRED_MINUTES = 5;
    private final Verifier verifier;
    private final String requestId;
    private final String body;


    public WechatPayValidator(Verifier verifier, String requestId, String body) {
        this.verifier = verifier;
        this.requestId = requestId;
        this.body = body;
    }

    protected static IllegalArgumentException parameterError(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("parameter error: " + message);
    }

    protected static IllegalArgumentException verifyFail(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("signature verify fail: " + message);
    }

    public final boolean validate(HttpServletRequest request) {
        try {
            //处理请求参数
            validateParameters(request);

            //构造验签名串
            String message = buildMessage(request);

            String serial = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);

            //验签
            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
                        serial, message, signature, requestId);
            }
        } catch (IllegalArgumentException e) {
            log.warn(e.getMessage());
            return false;
        }

        return true;
    }

    private void validateParameters(HttpServletRequest request) {

        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};

        String header = null;
        for (String headerName : headers) {
            header = request.getHeader(headerName);
            if (header == null) {
                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
            }
        }

        //判断请求是否过期
        String timestampStr = header;
        try {
            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
            // 拒绝过期请求
            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
            }
        } catch (DateTimeException | NumberFormatException e) {
            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
        }
    }

    private String buildMessage(HttpServletRequest request) {
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        return timestamp + "\n"
                + nonce + "\n"
                + body + "\n";
    }

    private String getResponseBody(CloseableHttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
    }

    /**
     * 对称解密,异步通知的加密数据
     *
     * @param resource 加密数据
     * @param apiV3Key apiV3密钥
     * @param type     1-支付,2-退款
     * @return
     */
    public static Map<String, Object> decryptFromResource(String resource, String apiV3Key, Integer type) {

        String msg = type == 1 ? "支付成功" : "退款成功";
        log.info(msg + ",回调通知,密文解密");
        try {
            //通知数据
            Map<String, String> resourceMap = JSONObject.parseObject(resource, new TypeReference<Map<String, String>>() {
            });
            //数据密文
            String ciphertext = resourceMap.get("ciphertext");
            //随机串
            String nonce = resourceMap.get("nonce");
            //附加数据
            String associatedData = resourceMap.get("associated_data");

            log.info("密文: {}", ciphertext);
            AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
            String resourceStr = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    ciphertext);

            log.info(msg + ",回调通知,解密结果 : {}", resourceStr);
            return JSONObject.parseObject(resourceStr, new TypeReference<Map<String, Object>>() {
            });
        } catch (Exception e) {
            throw new RuntimeException("回调参数,解密失败!");
        }
    }


    /**
     * 将通知参数转化为字符串
     *
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

编写下单 退款 controller    这里注意 微信用户openid   

package com.example.pay.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.example.pay.config.WechatPayConfig;
import com.example.pay.config.WechatPayRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @ClassName: pay
 * @author: tang
 * @createTime 2023-08-16
 */
@Slf4j
@RestController
@RequestMapping("/pay")
public class PayController {


    @Resource
    private WechatPayConfig wechatPayConfig;

    @Resource
    private WechatPayRequest wechatPayRequest;

    /**
     * 预支付订单生成入口
     */
    @GetMapping("/transactions")
    public Map<String, Object> transactions() {
      
        // 统一参数封装
        Map<String, Object> params = new HashMap<>(10);
        // 1,appid:公众号或移动应用的唯一标识符。
        params.put("appid", wechatPayConfig.getAppId());
        // 2,mch_id:商户号,由微信支付分配。
        params.put("mchid", wechatPayConfig.getMchId());
        // 3.description body:商品描述。
        params.put("description", "奥迪a4l 2023-限量款");
        // 4.out_trade_no:商户订单号,由商户自定义。
        params.put("out_trade_no", "we56f45waf4w6a5fwa");
        // 5.notify_url:接收微信支付异步通知回调地址。
        params.put("notify_url", wechatPayConfig.getNotifyUrl());
        // 6.total_fee:订单总金额,单位为分。
        Map<String, Object> amountMap = new HashMap<>(4);
        // 金额单位为分
        amountMap.put("total", 999999);
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);


        // 7.openid:用户在商户appid下的唯一标识。
        Map<String, Object> payerMap = new HashMap<>(4);
        // openid  需要前端小程序通过用户code 请求微信接口获取用户唯一openid  不懂的看官方文档:https://developers.weixin.qq.com/doc/aispeech/miniprogram/quickuse.html
        payerMap.put("openid", "xxxxxxxxxxxxxxxxxxxx");
        params.put("payer", payerMap);

        String paramsStr = JSON.toJSONString(params);
        log.info("请求参数 ===> {}" + paramsStr);

        // 微信预支付下单接口路径
        String payUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        // 获取支付 prepay_id参数
        String resStr = wechatPayRequest.wechatHttpOrderPost(payUrl, paramsStr);


        Map<String, Object> resMap = JSONObject.parseObject(resStr, new TypeReference<Map<String, Object>>() {
        });
        Object prepayId = resMap.get("prepay_id");

        // 得到当前系统时间搓
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);

        // 获取签名
        String paySign;
        try {
            StringBuilder sb = new StringBuilder();
            // 应用id
            sb.append(wechatPayConfig.getAppId()).append("\n");
            // 支付签名时间戳
            sb.append(timeStamp).append("\n");
            // 随机字符串
            sb.append("5w7er7wa4fwa5e").append("\n");
            // 预支付交易会话ID  这个要注意 key = "prepay_id=xxxxxx"
            sb.append("prepay_id=").append(prepayId).append("\n");
            // 签名
            Signature sign = Signature.getInstance("SHA256withRSA");
            // 获取商户私钥并进行签名
            PrivateKey privateKey = wechatPayConfig.getPrivateKey(wechatPayConfig.getKeyPemPath());
            sign.initSign(privateKey);
            sign.update(sb.toString().getBytes(StandardCharsets.UTF_8));
            // 得到签名
            paySign = Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            log.error("支付模块_生成交易签名失败!" + e);
            return new HashMap<>();
        }


        // 将签名时数据和签名一起返回前端用于前端吊起支付
        Map<String, Object> map = new HashMap<>();
        // 小程序id
        map.put("appId", wechatPayConfig.getAppId());
        // 时间戳
        map.put("timeStamp", timeStamp);
        // 随机字符串
        map.put("nonceStr", "56523268632356");
        // 预支付交易会话ID
        map.put("package", "prepay_id=" + prepayId);
        // 签名方式
        map.put("signType", "RSA");
        // 签名
        map.put("paySign", paySign);
        return map;
    }


    /**
     * 申请退款
     */
    @GetMapping("/refundOrder")
    public String refundOrder() {

        log.info("根据订单号申请退款,订单号: {}", "要退款的订单号  这里写死");
        // 退款请求路径
        String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
        // 设置参数
        Map<String, Object> params = new HashMap<>(2);
        // 要退款的订单编号订单编号
        params.put("out_trade_no", "57984wera64");
        // 商户自定义退款记录单号 用于退款记录的单号 跟退款订单号不是一样的
        int outRefundNo = new Random().nextInt(999999999);
        log.info("退款申请号:{}", outRefundNo);
        params.put("out_refund_no", outRefundNo + "");
        // 退款原因
        params.put("reason", "申请退款");
        // 退款通知回调地址
        params.put("notify_url", wechatPayConfig.getRefundNotifyUrl());

        Map<String, Object> amountMap = new HashMap<>();
        //退款金额,单位:分
        amountMap.put("refund", 999999);
        //原订单金额,单位:分
        amountMap.put("total", 99999);
        //退款币种
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);
        String paramsStr = JSON.toJSONString(params);
        // todo 插入一条退款记录到数据库
        log.info("请求参数 ===> {}" + paramsStr);
        String res = wechatPayRequest.wechatHttpPost(url, paramsStr);
        log.info("退款结果:{}", res);
        return res;
    }


}

支付 退款 回调controller

package com.example.pay.controller;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.example.pay.config.WechatPayConfig;
import com.example.pay.config.WechatPayValidator;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName: NotifyController
 * @author: tang
 * @createTime 2023-08-10
 */

@RestController
@Slf4j
@RequestMapping("/callback")
public class CallbackController {

    @Resource
    private WechatPayConfig wechatPayConfig;

    @Resource
    private Verifier verifier;

    private final ReentrantLock lock = new ReentrantLock();


    /**
     * 支付回调处理
     *
     * @param request
     * @param response
     * @return
     */
    @PostMapping("/payNotify")
    public Map<String, String> payNotify(HttpServletRequest request, HttpServletResponse response) {
        log.info("支付回调");

        // 处理通知参数
        Map<String, Object> bodyMap = getNotifyBody(request);
        if (bodyMap == null) {
            return falseMsg(response);
        }

        log.warn("=========== 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱 ===========");
        if (lock.tryLock()) {
            try {
                // 解密resource中的通知数据
                String resource = bodyMap.get("resource").toString();
                Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(), 1);
                String orderNo = resourceMap.get("out_trade_no").toString();
                // String transactionId = resourceMap.get("transaction_id").toString();
                // 更改状态 获取订单号  修改订单状态为已支付

                // TODO 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
                log.warn("=========== 根据订单号,做幂等处理 ===========");
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }

        //成功应答
        return trueMsg(response);
    }

    /**
     * 退款回调处理
     *
     * @param request
     * @param response
     * @return
     */
    @PostMapping("/refundNotify")
    public Map<String, String> refundNotify(HttpServletRequest request, HttpServletResponse response) {
        log.info("退款回调");

        // 处理通知参数
        Map<String, Object> bodyMap = getNotifyBody(request);
        if (bodyMap == null) {
            return falseMsg(response);
        }

        log.warn("=========== 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱 ===========");
        if (lock.tryLock()) {
            try {
                // 解密resource中的通知数据
                String resource = bodyMap.get("resource").toString();
                Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(), 2);
                String orderNo = resourceMap.get("out_trade_no").toString();
//                String transactionId = resourceMap.get("transaction_id").toString();
                log.info("退款所有参数" + resourceMap);

                // TODO 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
                // 更改订单状态为已退款
                log.warn("=========== 根据订单号,做幂等处理 ===========");
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }

        //成功应答
        return trueMsg(response);
    }

    private Map<String, Object> getNotifyBody(HttpServletRequest request) {
        //处理通知参数
        String body = WechatPayValidator.readData(request);
        log.info("退款回调参数:{}", body);

        // 转换为Map
        Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>() {
        });
        // 微信的通知ID(通知的唯一ID)
        String notifyId = bodyMap.get("id").toString();
        // 验证签名信息
        WechatPayValidator wechatPayValidator
                = new WechatPayValidator(verifier, notifyId, body);
        if (!wechatPayValidator.validate(request)) {

            log.error("通知验签失败");
            return null;
        }
        log.info("通知验签成功");
        return bodyMap;
    }

    private Map<String, String> falseMsg(HttpServletResponse response) {
        Map<String, String> resMap = new HashMap<>(8);
        //失败应答
        response.setStatus(500);
        resMap.put("code", "ERROR");
        resMap.put("message", "通知验签失败");
        return resMap;
    }

    private Map<String, String> trueMsg(HttpServletResponse response) {
        Map<String, String> resMap = new HashMap<>(8);
        //成功应答
        response.setStatus(200);
        resMap.put("code", "SUCCESS");
        resMap.put("message", "成功");
        return resMap;
    }
}

整体项目结构

java 微信支付v3,微信,微信小程序,小程序

请求预支付订单 得到预支付订单参数 小程序拿着这些参数拉起支付页面进行支付

java 微信支付v3,微信,微信小程序,小程序

支付完成后 微信会回调我们预留的回调接口 

这里使用的是花生壳内网穿透进行回调测试的 

退款业务就不一一截图了  图片太多显得文章拉胯

java 微信支付v3,微信,微信小程序,小程序文章来源地址https://www.toymoban.com/news/detail-740585.html


总结

到了这里,关于【微信支付】java-微信小程序支付-V3接口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python对接微信小程序V3接口进行支付,并使用uwsgi+nginx+django进行https部署

    网上找了很多教程,但是很乱很杂,并且教程资源很少且说的详细。这里就记录一下分享给大家 共分为以下几个步骤: 目录 一、开始前准备信息 二、使用前端code获取用户的openid 三、对接小程序v3接口下单 四、小程序支付的回调 五、安装并启动uwsgi 六、安装并启动nginx 七、

    2024年02月12日
    浏览(44)
  • SpringBoot整合微信小程序支付V3(支付、退款)

    微信支付开发前,需要先获取商家信息,包括商户号、AppId、证书和密钥。 获取商户号 微信商户平台 申请成为商户 = 提交资料 = 签署协议 = 获取商户号 获取AppID 微信公众平台 注册服务号 = 服务号认证 = 获取APPID = 绑定商户号 申请商户证书 登录商户平台 = 选择 账户中心 = 安

    2024年02月08日
    浏览(44)
  • springboot实现微信小程序V3微信支付功能

    appId:小程序appid appSecret:小程序的secret mchId:商户号 keyPath:商户私钥路径(apiclient_key.pem) certPath:证书路径(apiclient_cert.pem) platFormPath:平台证书(cert.pem) 注 : 需要通过写程序生成平台证书(见v3Get()方法) apiKey3:apiv3密钥 serialnumber:商户证书序列号 notifyUrl:回调地

    2024年02月12日
    浏览(61)
  • SpringBoot 整合微信小程序微信支付V3 jsapi (支付、退款)

    最近的一个微信小程序项目里有用到微信支付,网上找的资料都是特别乱,看起来特别懵,结合了好多文章的内容,终于做了出来,可能我的这个博文看起来也是特别乱,但是是可以直接C走简单改一改就可以用的。(支付成功回调,和退款回调因为昨天刚在阿里申请的域名还

    2024年04月25日
    浏览(50)
  • springboot整合IJPay实现微信支付-V3---微信小程序

    微信支付适用于许多场合,如小程序、网页支付、但微信支付相对于其他支付方式略显麻烦,我们使用IJpay框架进行整合 JPay 让支付触手可及, 封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。不依赖任何第三方 mvc 框架,仅仅作为工具使用简单

    2024年02月02日
    浏览(70)
  • 小程序微信支付V3版本Java集成

    相较于之前的微信支付API,主要区别是: 遵循统一的REST的设计风格 使用JSON作为数据交互的格式,不再使用XML 使用基于非对称密钥的SHA256-RSA的数字签名算法,不再使用MD5或HMAC-SHA256 不再要求携带HTTPS客户端证书(仅需携带证书序列号) 使用AES-256-GCM,对回调中的关键信息进

    2024年02月11日
    浏览(62)
  • java微信支付v3系列——5.微信支付成功回调

    java微信支付v3系列——1.微信支付准备工作 java微信支付v3系列——2.微信支付基本配置 java微信支付v3系列——3.订单创建准备操作 java微信支付v3系列——4.创建订单的封装及使用 java微信支付v3系列——5.微信支付成功回调 java微信支付v3系列——6.微信支付查询订单API java微信支

    2024年01月17日
    浏览(44)
  • Java实现微信支付v3的支付回调

    以前都是自己手搓api的, 现在有轮子了, 尝试记录一下如何使用 我的做法是首先打开v3的代码仓库, 直接进去看看他们的文档, 可以看到这么一坨东西 开发前准备 2. 先引入maven 初始化商户配置 先从请求头中获取构建RequestParam需要的参数 初始化解析器 进行验签, 解密并转换成

    2024年02月12日
    浏览(53)
  • java微信支付v3系列——6.微信支付查询订单API

    java微信支付v3系列——1.微信支付准备工作 java微信支付v3系列——2.微信支付基本配置 java微信支付v3系列——3.订单创建准备操作 java微信支付v3系列——4.创建订单的封装及使用 java微信支付v3系列——5.微信支付成功回调 java微信支付v3系列——6.微信支付查询订单API java微信支

    2023年04月08日
    浏览(41)
  • PHP实现小程序微信支付(v3版本)

    PS:本篇文章是PHP对小程序进行微信支付v3版本的实现,仅用于对支付流程的了解,具体使用方面需要大家自行调整 小程序端JS代码: PHP类的相关代码:

    2024年02月12日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包