微信支付JSAPI支付方式,支付和退款的详细代码以及解析

最近开发微信支付功能,但是个人看来看微信支付的文档很是头疼,上面提供的demo,复制下来不能用,又咨询客服查询资料才把微信支付搞清楚。

我使用的是JSAPI的 支付方法,这个支付方法是以下流程:

支付流程:

获取预支付订单号和签名(后端)----->前端拿到预支付订单号和签名调器支付---->支付成功后自动回调

退款流程:

调用企业微信申请退款接口----->退款成功后自动回调

在开发之前需要做好基本配置:

(1)引入微信支付依赖:

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

(2)配置商户证书和APIv3密钥,这部操作直接看文档即可,已经非常详细:下载并配置商户证书 - 通用规则 | 微信支付商户文档中心

1.支付流程代码:

下面代码一些商户信息,商户私密,appid,openid根据自己的实际情况来替换代码。

import com.wechat.pay.java.core.util.IOUtil;
import com.wechat.pay.java.core.util.PemUtil;

import java.security.PrivateKey;

public class MtWeChatConstant {

    //商户号  (自己商户平台的商户号)
    public static final String MERCHANT_ID = "1234568951";
    //商户API私钥路径
    public static final String MERCHANT_PRIVATE_KEY_PATH = "/app/resources/wxPayConfig/apiclient_key.pem";
    //商户证书序列号(自己配置的证书序列号)
    public static final String MERCHANT_SERIAL_NUMBER = "ABCDEF**M63BD96AA3F7A307AEA";
    //商户APIV3密钥(自己配置的APIV3)
    public static final String API_V3_KEY = "cewi123***89hUPOmAWgABCABCMGH3";
    //支付完成后的回调的地址(如果不要回调信息,可以不配置)
    public static final String PAY_NOTIFY_URL = "https://xxxx.xxxx.com/services/mt/api/pay/notifi";
    //退款完成后的回调的地址(如果不要回调信息,可以不配置)
    public static final String REFUND_NOTIFY_URL = "https://xxx.xxxxx.com/services/mt/api/refund/notifi";
    //一下代码用于生成签名,写法参考微信支付文档,链接:https://github.com/wechatpay-apiv3/wechatpay-java/blob/main/core/src/test/java/com/wechat/pay/java/core/model/TestConfig.java
    public static final PrivateKey MERCHANT_PRIVATE_KEY;
    public static final String MERCHANT_PRIVATE_KEY_STRING;

    static {
        try {
            MERCHANT_PRIVATE_KEY_STRING = IOUtil.loadStringFromPath(MERCHANT_PRIVATE_KEY_PATH);
            MERCHANT_PRIVATE_KEY = PemUtil.loadPrivateKeyFromString(MERCHANT_PRIVATE_KEY_STRING);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

这个配置商户证书的微信支付平台提供的证书私钥,是我存放的位置:"/app/resources/wxPayConfig/apiclient_key.pem" 根据自己项目实际的情况来存放

获取预支付订单号以及签名的代码:

import com.alibaba.fastjson.JSON;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class PerPayId {
    public static void main(String[] args) {
        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(MtWeChatConstant.MERCHANT_ID)
                .privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
                .merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
                .apiV3Key(MtWeChatConstant.API_V3_KEY)
                .build();

        //获取预支付订单号
        // 构建service
        JsapiService service = new JsapiService.Builder().config(config).build();
        // request.setXxx(val)设置所需参数,具体参数可见Request定义,参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/direct-jsons/jsapi-prepay.html
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        //设置金额, 以分为单位 金额根据实际情况自行填写
        amount.setTotal(1);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        //绑定微信支 公众号的appid 根据实际情况自行获取填写
        request.setAppid("appid");
        // 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,根据自己需求自己定义
        request.setMchid(MtWeChatConstant.MERCHANT_ID);
        request.setDescription("产品描述");
        //支付成功后回调的URL
        request.setNotifyUrl(MtWeChatConstant.PAY_NOTIFY_URL);
        //ZHE
        request.setOutTradeNo(UUID.randomUUID().toString().replace("-",""));
        //自定义信息, 在支付订单回调可以取到(自行替换,如不需要可注释掉)
        Map meetIfno = new HashMap();
        meetIfno.put("key", "value");
        request.setAttach(JSON.toJSONString(meetIfno));
        Payer payer = new Payer();
        //支付人员的的openid
        payer.setOpenid("openid");
        request.setPayer(payer);
        //调用支付接口
        PrepayResponse response = service.prepay(request);
        //响应体中有预支付订单号
        response.getPrepayId();
        System.out.println(response.getPrepayId());
        //以上为获取预支付订单号步骤


        /**
         * 下面为使用加密获取密钥,也需要返回给前段
         */
        long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        //构造签名参数(appid更换为实际支付人员的appid)
        String message = "appid" + "\n" + timeStamp + "\n" + nonceStr + "\n" + "prepay_id=" + response.getPrepayId() + "\n";

        //生成签名
        Signer rsaSigner = new RSASigner(MtWeChatConstant.MERCHANT_SERIAL_NUMBER, MtWeChatConstant.MERCHANT_PRIVATE_KEY);
        String sign = rsaSigner.sign(message).getSign();
        //前段通过该签名可以调用支付
        System.out.println(sign);

        /**
         * 注意:
         * 1.预支付订单号(prepay_id),以及生成签名的timeStamp,nonceStr的参数都需要返回前端
         * 2.在构造签名参数的时候,\n一定要加 否则也是签名无效
         * 3.
         */
    }

2.如果支付完成需要回调的话,以下是回调的代码:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.e7ing.smart.constant.MtWeChatConstant;
import com.e7ing.smart.utils.E7ResponseEntity;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.model.TransactionPayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
@Transactional(rollbackFor = Exception.class)
public class WeChatPayInfoController {

    private final Logger log = LoggerFactory.getLogger(WeChatPayInfoController.class);

    /**
     * 处理订单回调事件
     *
     * @param info
     * @return
     */
    @PostMapping("/pay/notifi")
    public String savePayOrder(
            @RequestHeader(value = "Wechatpay-Signature") String sign,
            @RequestHeader(value = "Wechatpay-Serial") String serial,
            @RequestHeader(value = "Wechatpay-Nonce") String nonce,
            @RequestHeader(value = "Wechatpay-Timestamp") String timestamp,
            @RequestHeader(value = "Wechatpay-Signature-Type") String signType,
            @RequestBody String info
    ) {
        /**
         * 注意:
         * 1.请求头必须按照代码中的定义格式(如果接收不到可以试试@RequestHeader(value = "Wechatpay-Signature",required = false) String sign)在里面加一个required = false
         * 2.接收body请求体必须是String类型,不能使用json,如果使用json会导致解析不正确
         * 3.返回值类型可以参考文档,我这边定义的String,看文档需求是成功返回"SUCCESS",失败为"FAIL"
         * 3.其他问题可以参考文档代码:https://github.com/wechatpay-apiv3/wechatpay-java/tree/main
         * 4.解析出来的参数问题可以参考这个文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html
         */

        log.debug(
                "获取到回调事件,请求头信息 sign:{}, serial:{}, nonce:{}, timestamp:{}, signType:{}, info:{}",
                sign,
                serial,
                nonce,
                timestamp,
                signType,
                info
        );

        // 构造 RequestParam
        com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(sign)
                .timestamp(timestamp)
                .body(info)
                .build();

        // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(MtWeChatConstant.MERCHANT_ID)
                .privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
                .merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
                .apiV3Key(MtWeChatConstant.API_V3_KEY)
                .build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);

        // 以支付通知回调为例,验签、解密并转换成 Transaction
        Transaction payInfo = null;
        try {
            //回调所有的支付信息
            payInfo = parser.parse(requestParam, Transaction.class);
        } catch (Exception e) {
            log.error(
                    "签名验证失败,获取到回调事件,请求头信息 sign:{}, serial:{}, nonce:{}, timestamp:{}, signType:{}, info:{}",
                    sign,
                    serial,
                    nonce,
                    timestamp,
                    signType,
                    info
            );
            log.error(e.getMessage());
            return "FAIL";
        }
        //获取支付人员的信息
        TransactionPayer payerInfo = payInfo.getPayer();
        //获取自定义信息
        JSONObject meetInfo = JSON.parseObject(payInfo.getAttach());
        //具体信息自行获取即可
        System.out.println(payInfo.toString());
        
        /**
         * 以上代码只是拿到回调的签名,解析出来回调的支付信息,具体业务根据自己的实际情况自行编写
         */
        return "SUCCESS";
    }
}

3.申请退款代码:

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;

public class Refund {
    public static void main(String[] args) {
        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(MtWeChatConstant.MERCHANT_ID)
                .privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
                .merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
                .apiV3Key(MtWeChatConstant.API_V3_KEY)
                .build();
        //构造申请退款对象
        RefundService service = new RefundService.Builder().config(config).build();
        //请求参数
        CreateRequest request = new CreateRequest();
        //设置退款金额 根据自己的实际业务自行填写
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(1L);
        amountReq.setTotal(1L);
        amountReq.setCurrency("CNY");
        request.setAmount(amountReq);
        //支付成功后回调回来的transactionId 按照实际情况填写
        request.setTransactionId("transactionId");
        //支付成功后回调回来的transactionId 按照实际情况填写
        request.setOutRefundNo("transactionId");
        //退款成功的回调地址
        request.setNotifyUrl(MtWeChatConstant.REFUND_NOTIFY_URL);
        //发起请求,申请退款
        com.wechat.pay.java.service.refund.model.Refund refund = service.create(request);
        //调用成功的响应数据
        System.out.println(refund);
        /**
         * 注意:
         * 1.以上参数都需要按照实际情况填写,大部分参数都是付款成功之后的回调数据
         * 2.以上退款是必要参数,如果想要填写其他参数参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/create.html
         * 3.SDK的微信支付的退款代码参考:https://github.com/wechatpay-apiv3/wechatpay-java/blob/main/service/src/main/java/com/wechat/pay/java/service/refund/RefundService.java
         */
    }

4.退款成功的回调代码:

import com.e7ing.smart.constant.MtWeChatConstant;
import com.e7ing.smart.utils.E7ResponseEntity;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
@Transactional(rollbackFor = Exception.class)
public class WeChatPayInfoController {

    private final Logger log = LoggerFactory.getLogger(WeChatPayInfoController.class);
    /**
     * 处理退款订单回调事件
     *
     * @param info
     * @return
     */
    @PostMapping("/refund/notifi")
    public E7ResponseEntity saveReFundOrder(
            @RequestHeader(value = "Wechatpay-Signature", required = false) String sign,
            @RequestHeader(value = "Wechatpay-Serial", required = false) String serial,
            @RequestHeader(value = "Wechatpay-Nonce", required = false) String nonce,
            @RequestHeader(value = "Wechatpay-Timestamp", required = false) String timestamp,
            @RequestHeader(value = "Wechatpay-Signature-Type", required = false) String signType,
            @RequestBody String info
    ) {
        /**
         * 注意:
         * 1.请求头必须按照代码中的定义格式
         * 2.接收body请求体必须是String类型,不能使用json,如果使用json会导致解析不正确
         * 3.返回值类型可以参考文档,我这边定义的String,看文档需求是成功返回"SUCCESS",失败为"FAIL"
         * 3.其他问题可以参考文档代码:https://github.com/wechatpay-apiv3/wechatpay-java/tree/main
         * 4.解析出来的参数问题可以参考这个文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html
         */

        log.debug(
                "获取到回调事件,请求头信息 sign:{}, serial:{}, nonce:{}, timestamp:{}, signType:{}, info:{}",
                sign,
                serial,
                nonce,
                timestamp,
                signType,
                info
        );

        // 构造 RequestParam
        com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(sign)
                .timestamp(timestamp)
                .body(info)
                .build();

        // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(MtWeChatConstant.MERCHANT_ID)
                .privateKeyFromPath(MtWeChatConstant.MERCHANT_PRIVATE_KEY_PATH)
                .merchantSerialNumber(MtWeChatConstant.MERCHANT_SERIAL_NUMBER)
                .apiV3Key(MtWeChatConstant.API_V3_KEY)
                .build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        //获取退款回调的信息
        RefundNotification refundInfo = parser.parse(requestParam, RefundNotification.class);
        System.out.println(refundInfo);
        /**
         * 以上代码用于获取退款回调的信息,写法与支付回调很像,具体业务根据自己实际情况修改
         * 回调的具体参数参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
         */
        return new E7ResponseEntity("SUCCESS");
    }
}

JSAPI其他的功能,以及APP支付,H5支付,Native支付等等都可以参考,微信支付SDK全部都封装好了,使用方法大同小异。

微信支付的官方文档:API列表 - JSAPI支付 | 微信支付商户文档中心

微信支付SDK官方文档:GitHub - wechatpay-apiv3/wechatpay-java: 微信支付 APIv3 的官方 Java Library

以上代码为本人编写,如果有哪里涉及到侵权请联系本人删改。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个简单的Java Spring Boot微信支付示例,使用APIv2 JSAPI支付代码: 1. 创建微信支付配置类 ```java @Configuration public class WechatPayConfig { @Value("${wechat.pay.appId}") private String appId; @Value("${wechat.pay.mchId}") private String mchId; @Value("${wechat.pay.apiKey}") private String apiKey; @Bean public WXPay wxPay() throws Exception { WXPayConfigImpl config = WXPayConfigImpl.getInstance(); config.setAppID(appId); config.setMchID(mchId); config.setKey(apiKey.getBytes()); return new WXPay(config); } } ``` 2. 创建微信支付控制器 ```java @RestController @RequestMapping("/api/wechat/pay") public class WechatPayController { @Autowired private WXPay wxPay; @PostMapping("/unifiedorder") public ResponseEntity<?> unifiedOrder(@RequestBody WechatPayRequest request, HttpServletRequest httpRequest) { try { // 构造支付请求参数 Map<String, String> data = new HashMap<>(); data.put("body", request.getBody()); data.put("out_trade_no", request.getOutTradeNo()); data.put("total_fee", request.getTotalFee().toString()); data.put("spbill_create_ip", httpRequest.getRemoteAddr()); data.put("notify_url", "https://your-notify-url.com"); data.put("trade_type", "JSAPI"); data.put("openid", request.getOpenid()); // 调用统一下单API Map<String, String> result = wxPay.unifiedOrder(data); // 生成JSAPI支付参数 String prepayId = result.get("prepay_id"); Map<String, String> jsApiParams = new HashMap<>(); jsApiParams.put("appId", wxPay.getConfig().getAppID()); jsApiParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); jsApiParams.put("nonceStr", WXPayUtil.generateNonceStr()); jsApiParams.put("package", "prepay_id=" + prepayId); jsApiParams.put("signType", "MD5"); String paySign = WXPayUtil.generateSignature(jsApiParams, wxPay.getConfig().getKey()); jsApiParams.put("paySign", paySign); return ResponseEntity.ok(jsApiParams); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } } } ``` 3. 创建微信支付请求类 ```java public class WechatPayRequest { private String body; private String outTradeNo; private BigDecimal totalFee; private String openid; // getters and setters } ``` 4. 在application.properties文件中添加微信支付配置 ```properties wechat.pay.appId=your-appId wechat.pay.mchId=your-mchId wechat.pay.apiKey=your-apiKey ``` 5. 使用Postman等工具测试JSAPI支付接口 发送POST请求到http://localhost:8080/api/wechat/pay/unifiedorder,请求体如下: ```json { "body": "测试支付", "outTradeNo": "20220101000001", "totalFee": 1, "openid": "your-openid" } ``` 其中,openid是JSAPI支付必须的参数,可以通过微信OAuth2.0授权获取。 返回结果如下: ```json { "appId": "your-appId", "timeStamp": "1641260647", "nonceStr": "9jvOvEJZzVw2STN5", "package": "prepay_id=wx01155047389241f9c9ab15d8d8e9813000", "signType": "MD5", "paySign": "B24F9D7F2DBE3048F7BA4D400C4B0B4B" } ``` 将返回结果中的数据传递给微信JSAPI支付接口即可完成支付
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值