微信支付代码
🍚 SpringBoot 🎯 2026-05-10 🔥 8

官网和文档

https://pay.weixin.qq.com/

https://pay.weixin.qq.com/docs/merchant/development/interface-rules/introduction.html

SDK

https://github.com/wechatpay-apiv3

授权登录

获取手机号和openId,便于支付

<!-- 登录 -->
<u-popup :show="login.show" mode="center" :closeOnClickOverlay="false" customStyle="margin: 0 40rpx" :round="4">
    <view class="login-box">
        <view class="login-title">登录提醒</view>
        <view class="login-content">为了给您提供更好的服务,请先进行授权登录,绑定您的个人数据,同时请认真阅读《用户协议》和《隐私协议》</view>
        <view class="login-option">
            <button @click="login.show = false">取消</button>
            <button open-type="getPhoneNumber" @getphonenumber="phonenumber">登录</button>
        </view>
    </view>
</u-popup>


// 登录
phonenumber(e) {
    // 获取登录令牌获取openId
    let that = this
    wx.login({
        success(res) {
            console.log(res);
            if (res.code) {
                httpUtil('/authorization', 'POST', {
                    code: e.detail.code,
                    openIdCode: res.code
                }, (res) => {
                    if (res.code == 200) {
                        uni.setStorageSync('token', res.data.token)
                        uni.setStorageSync('userId', res.data.userId)
                        uni.setStorageSync('phone', res.data.phone)
                        uni.setStorageSync('createTime', res.data.createTime)
                        uni.setStorageSync('photo', res.data.photo)
                        that.login.show = false
                        uni.reLaunch({
                            url: '/pages/home/home'
                        })
                    } else {
                        that.$refs.uToast.show({
                            message: res.msg,
                        })
                    }
                }, (err) => {
                    that.$refs.uToast.show({
                        message: '服务异常',
                    })
                })
            } else {
                console.log('授权失败!' + res.errMsg)
            }
        }
    })
},

后端处理

Override
public Result<AuthorizationRespVo> authorization(AuthorizationReqVo params) {
    // 获取手机号
    String wxToken = getWxToken();
    WxGetPhoneVo phone = getPhone(wxToken, params.getCode());
    Criteria criteria = new Criteria();
    criteria.and(FieldUtil.get(User::getPhone)).is(phone.getPhone());
    criteria.and(FieldUtil.get(User::getStatus)).is(GlobalStatusEnum.STATUS_NORMAL.value());
    // TODO 拉黑人员处理
    User user = mongoTemplate.findOne(new Query(criteria), User.class);
    if (Objects.isNull(user)) {
        WxCode2SessionVo wxCode2SessionVo = openId(params.getOpenIdCode());
        // 没有则注册
        user = new User();
        user.setPhone(phone.getPhone());
        user.setOpenId(wxCode2SessionVo.getOpenid());
        user.setStatus(GlobalStatusEnum.STATUS_NORMAL.value());
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        mongoTemplate.save(user);
    }
    // 缓存用户信息对象
    UserCacheVo userCacheVo = new UserCacheVo();
    userCacheVo.setPhone(user.getPhone());
    userCacheVo.setId(user.getId().toString());
    String json;
    try {
        json = new ObjectMapper().writeValueAsString(userCacheVo);
    } catch (JsonProcessingException e) {
        log.info("登录成功后缓存对象转换异常:" + e);
        throw new RuntimeException();
    }
    // 缓存登录信息
    String token = new ObjectId().toString();
    redisTemplate.opsForValue().set(RedisKeyEnum.KEY_USER_TOKEN.value() + token, json, 30, TimeUnit.DAYS);
    // 返回信息
    AuthorizationRespVo authorizationRespVo = new AuthorizationRespVo();
    authorizationRespVo.setId(user.getId().toString());
    authorizationRespVo.setToken(token);
    authorizationRespVo.setPhone(user.getPhone());
    authorizationRespVo.setCreateTime(user.getCreateTime());
    authorizationRespVo.setPhoto(user.getPhoto());
    authorizationRespVo.setUserId(user.getId().toString());
    return Result.data("登录成功", authorizationRespVo);
}


/**
 * 获取微信凭证
 */
private String getWxToken() {
    // 优先从缓存读取
    String accessToken = redisTemplate.opsForValue().get(RedisKeyEnum.KEY_WEIXIN_ACCESS_TOKEN.value());
    if (Objects.isNull(accessToken)) {
        String url = "https://api.weixin.qq.com/cgi-bin/stable_token";
        JsonObject json = new JsonObject();
        json.addProperty("grant_type", "client_credential");
        json.addProperty("appid", "xxxx"); // appid
        json.addProperty("secret", "xxxx"); // secret 从微信小程序开发管理中生成
        log.debug(String.valueOf(json));
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        HttpEntity<String> formEntity = new HttpEntity<>(json.toString(), headers);
        String sdResponse = restTemplate.postForEntity(url, formEntity, String.class).getBody();
        log.debug(sdResponse);
        if (Objects.isNull(sdResponse)) {
            throw new RuntimeException("获取微信凭证失败");
        }
        accessToken = JsonParser.parseString(sdResponse).getAsJsonObject().get("access_token").getAsString();
        log.debug(accessToken);
        if (Objects.isNull(accessToken)) {
            throw new RuntimeException("获取微信凭证无法解析Token");
        }
        // 缓存
        redisTemplate.opsForValue().set(RedisKeyEnum.KEY_WEIXIN_ACCESS_TOKEN.value(), accessToken, 7000, TimeUnit.SECONDS);
    }
    return accessToken;
}

 /**
 * 根据动态令牌获取手机号
 */
private WxGetPhoneVo getPhone(String accessToken, String code) {
    String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
    JsonObject json = new JsonObject();
    json.addProperty("code", code);
    log.debug(String.valueOf(json));
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
    headers.setContentType(type);
    headers.add("Accept", MediaType.APPLICATION_JSON.toString());
    HttpEntity<String> formEntity = new HttpEntity<>(json.toString(), headers);
    String sdResponse = restTemplate.postForEntity(url, formEntity, String.class).getBody();
    log.debug(sdResponse);
    if (Objects.isNull(sdResponse)) {
        throw new RuntimeException("根据code获取手机号失败");
    }
    JsonObject phoneInfo = JsonParser.parseString(sdResponse).getAsJsonObject().get("phone_info").getAsJsonObject();
    // 手机号
    String phoneNumber = phoneInfo.get("purePhoneNumber").getAsString();
    // 国外手机号会有区号
    // String countryCode = phoneInfo.get("countryCode").getAsString();
    log.debug(phoneNumber);
    if (Objects.isNull(phoneNumber)) {
        throw new RuntimeException("根据code获取手机号无法解析");
    }
    // appId

    JsonObject watermark = phoneInfo.get("watermark").getAsJsonObject();
    String appid = watermark.get("appid").getAsString();
    WxGetPhoneVo wxGetPhoneVo = new WxGetPhoneVo();
    wxGetPhoneVo.setPhone(phoneNumber);
    wxGetPhoneVo.setAppid(appid);
    log.info("登录获取手机号:{}", wxGetPhoneVo);
    return wxGetPhoneVo;
}

/**
 * 从微信获取openid
 */
private WxCode2SessionVo openId(String code) {
    String url = "https://api.weixin.qq.com/sns/jscode2session";
    url += "?appid=xxxx";
    url += "&secret=xxxx";
    url += "&grant_type=authorization_code";
    url += "&js_code=" + code;
    log.info(url);
    RestTemplate restTemplate = new RestTemplate();
    String response = restTemplate.getForObject(url, String.class);
    log.info(response);
    return ObjAndJson.toObj(response, WxCode2SessionVo.class);
}

支付

package com.code.impl;

import com.code.comEnum.GlobalStatusEnum;
import com.code.commonVo.Result;
import com.code.entity.User;
import com.code.service.PayService;
import com.code.utils.UserInfoUtil;
import com.code.vo.weixin.WxPayOrderReqVo;
import com.code.vo.weixin.WxPayOrderRespVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonParser;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
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.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
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.bson.types.ObjectId;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.Date;
import java.util.Objects;

@Slf4j
@Service
public class PayServiceImpl implements PayService {

    private String privateKey = "-----BEGIN PRIVATE KEY-----\n" +
            "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDT9pVo21NwRkSx\n" +
            "9wHAVe80i2yeUZLJD4wI1Np/uCVYHGPXI+MyqxZgLVfbjXsLqCCFAkFS1e4ObA79\n" +
            "......" +
            "5xhcPP8DVt/HjnzIOsBJYv5Ah0JByGaHcPmPfDJyXIuic8pPGnlgK62uQN+ledei\n" +
            "dJGJHxZQInP/6D3GkIIX25yBSQGZYtRuUXZyvVLIcwKBgQCkJwgRBRJZ6ILkh7lg\n" +
            "ilIAepTG/bqBq8uIXKaGaSr8u+zZ+Q4pZgQfbDJeIbPtSRDntx8Npt7onE17idmF\n" +
            "70+MTk9pfp/LAsWj4N4ToCN3c7aHQYDqSbVfuOiRyk02fl255pkQ4sp4JNMRSj14\n" +
            "yK/SLWj7CnD9fLA4HzJuQMP6ZQ==\n" +
            "-----END PRIVATE KEY-----";

    private String mchId = "xxx";

    private String mchSerialNo = "xxxx";

    private String apiV3Key = "xxx";

    private String appid = "xxxx";

    @Resource
    private MongoTemplate mongoTemplate;

    @Override
    public Result<WxPayOrderRespVo> wxPay(WxPayOrderReqVo params) {
        try {
            // 查找用户,获取appId
            User user = mongoTemplate.findById(UserInfoUtil.userId(), User.class);
            if (Objects.isNull(user) || !Objects.equals(user.getStatus(), GlobalStatusEnum.STATUS_NORMAL.value())) {
                throw new RuntimeException("用户不存在");
            }



            // 加载商户私钥(privateKey:私钥字符串)
            PrivateKey merchantPrivateKey = PemUtil
                    .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));

            // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(
                            mchId,
                            new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
                    apiV3Key.getBytes(StandardCharsets.UTF_8)
            );

            // 初始化httpClient
            HttpClient httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier)).build();

            // 下单
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            // 请求参数
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("appid", appid)
                    .put("mchid", mchId)
                    .put("description", "会员购买")
                    .put("out_trade_no", new ObjectId().toString()) // 订单号
                    .put("notify_url", "https://fanmr.cn/test"); // 回调地址
            rootNode.putObject("amount")
                    .put("total", 1);
            rootNode.putObject("payer")
                    .put("openid", "oGeWs64ho4UzSGogobEoWfEySHaA");
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

            HttpResponse execute = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(execute.getEntity());
            log.info(bodyAsString);
            String prepay_id = JsonParser.parseString(bodyAsString).getAsJsonObject().get("prepay_id").getAsString();

            WxPayOrderRespVo wxPayOrderRespVo = new WxPayOrderRespVo();
            wxPayOrderRespVo.setAppId(appid);
            wxPayOrderRespVo.setPartnerid(mchId);
            wxPayOrderRespVo.setPrepayId(prepay_id);
            wxPayOrderRespVo.setNonceStr("c5sEwbaNPiXAF3iv");
            wxPayOrderRespVo.setTimeStamp(String.valueOf(new Date().getTime() / 1000));
            // 计算签名
            String signStr = appid + "\n" + wxPayOrderRespVo.getTimeStamp()
                    + "\n" + wxPayOrderRespVo.getNonceStr() + "\nprepay_id="
                    + wxPayOrderRespVo.getPrepayId() + "\n";
            wxPayOrderRespVo.setSign(sign(signStr.getBytes()));
            return Result.data(wxPayOrderRespVo);
        } catch (RuntimeException | IOException e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 加载证书
     */
    public PrivateKey privateKey() {
        // 加载证书
        byte[] bytes = privateKey.getBytes(StandardCharsets.UTF_8);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        return PemUtil.loadPrivateKey(byteArrayInputStream);
    }

    /**
     * 小程序签名计算
     */
    private String sign(byte[] message) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey());
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 签名验证器
     */
    public Verifier verifier() throws NotFoundException, IOException, GeneralSecurityException, HttpCodeException {
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                new PrivateKeySigner(mchSerialNo, privateKey())), apiV3Key.getBytes(StandardCharsets.UTF_8));
        return certificatesManager.getVerifier(mchId);
    }


    /**
     * 定时更新平台证书功能
     */
    public CloseableHttpClient getWxPayClient() throws GeneralSecurityException, NotFoundException, IOException, HttpCodeException {
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, privateKey())
                .withValidator(new WechatPay2Validator(verifier()));
        return builder.build();
    }

}

前端uniapp支付

payOrder() {
    httpUtil('/pay/order', 'POST', {
        buy: 1
    }, (res) => {
        console.log(res);
        // 调用支付
        uni.getProvider({
            service: 'payment',
            success: function(payRes) {
                if (~payRes.provider.indexOf('wxpay')) {
                    console.log('调用支付');
                    console.log(payRes);
                    uni.requestPayment({
                        "provider": "wxpay", //固定值为"wxpay"
                        "timeStamp": res.data.timeStamp,
                        "nonceStr": res.data.nonceStr,
                        "package": "prepay_id=" + res.data.prepayId,
                        "signType": "RSA",
                        "paySign": res.data.sign,
                        success: function(res2) {
                            console.log('支付成功', res2);
                        },
                        fail: function(err2) {
                            console.error('支付失败', err2);
                        }
                    });
                }
            }
        });
    })
},
👨‍💻自述
踏实走好脚下的路,热爱生活,坚持学习,怀揣的理想,终有一天会实现
🏝️目录