微信小程序支付后端(java)

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

1、相关controller

package org.jeecg.modules.business.dispatch.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.jeecg.modules.business.dispatch.utils.WechatPayUtil.*;

@RestController
@RequestMapping("/wx/payment")
@Slf4j
public class WxPayController {

//    @Value("${wx.pay.app-id}")
//    private String appId;
//
//    @Value("${wx.pay.secret}")
//    private String secret;
//
//    @Value("${wx.pay.mch-id}")
//    private String mchId;
//
//    @Value("${wx.pay.serial-no}")
//    private String serialNo;
//
//    @Value("${wx.pay.service-domain}")
//    private String serviceDomain;
//
//    @Value("${wx.pay.private-key-path}")
//    private String privateKeyPath;

//    @Value("${wx.pay.public-key-path}")
//    private String publicKeyPath;
    private String appId = "wx06e3492xxxxxxxxx";
    private String secret = "85837128xxxxxxxxxxxxxxxxxxxx";
    private String mchId = "1715xxxxxxxx9";
    private String serialNo = "332E1ED427B648Axxxxxxxxxxxxxxxxxxxxx";
    private String serviceDomain = "https://api.mch.weixin.qq.com";
    private String privateKeyPath = "xxxxxxxxx\apiclient_key.pem";
    private String publicKeyPath = "xxxxxxxxxx\pub_key.pem";
    private String certPath = "xxxxxxxxxxxx\apiclient_cert.pem";
    private String apiv3Key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    private String notifyUrl = "http://xxx.xxx.xxx/wx/payment/notify";
    private final PrivateKey privateKey;
    private final PublicKey publicKey;

    @Autowired
    private IJsUnloadmoneyService jsUnloadmoneyService;
    @Autowired
    private IJhDispatchService jhDispatchService;
    @Autowired
    private IJhDispatchmaterialService jhDispatchmaterialService;
    @Autowired
    private IUnloadtypeService unloadtypeService;
    @Autowired
    private IJcVehicleService jcVehicleService;


    public WxPayController() throws Exception {
        this.privateKey = loadPrivateKey(privateKeyPath);
        this.publicKey = readPEMPublicKey(publicKeyPath);
//        this.publicKey = loadPublicKey(certPath);
    }
    /**
     * 获取openId
     * @param code
     * @return
     * @throws IOException
     */
//    @IgnoreAuth
    @GetMapping(value = "/getOpenId")
    public Map<String,Object> getOpenId(@RequestParam(name = "code") String code) throws IOException {
        Map<String,Object> map = new HashMap<>();

        if (code != null && code.length() > 0) {
            String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId
                    + "&secret=" + secret
                    + "&js_code=" + code
                    + "&grant_type=authorization_code";
            HttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url);
            HttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            // 将响应实体转为字符串
            String result = EntityUtils.toString(entity);
            System.out.println("result======="+result);
            JSONObject json = JSONObject.parseObject(result);

            String openid = json.getString("openid");
            map.put("openId",openid);
            String sessionKey = json.getString("session_key");
            map.put("sessionKey",sessionKey);
        }
        return map;
    }

    /**
     * 创建订单
     * @param orderRequest
     * @param response
     * @return
     * @throws Exception
     */
    @PostMapping("/unifiedorder")
    public JSONObject unifiedOrder(@RequestBody OrderRequest orderRequest, HttpServletResponse response) throws Exception {
        System.out.println("openId======="+orderRequest.getOpenId());
        JSONObject result = WechatPayUtil.createUnifiedOrder(orderRequest, mchId, serialNo, privateKey, serviceDomain,appId,notifyUrl);
        return result;
    }

    //回调方法
    @IgnoreAuth
    @PostMapping("/notify")
    public String notify(@RequestBody String body,
                         @RequestHeader("Wechatpay-Timestamp") String timestamp,
                         @RequestHeader("Wechatpay-Nonce") String nonceStr,
                         @RequestHeader("Wechatpay-Signature") String signature,
                         HttpServletRequest request) throws GeneralSecurityException, IOException {
        System.out.println("body=========="+body);
        JSONObject resourceObj = JSONObject.parseObject(body);
        JSONObject jsonObject = JSONObject.parseObject(resourceObj.getString("resource"));
        byte [] associatedData = jsonObject.getString("associated_data").getBytes();
        byte [] nonce = jsonObject.getString("nonce").getBytes();
        String ciphertext = jsonObject.getString("ciphertext");
        //1 解密
        AesUtil aesUtil = new AesUtil(apiv3Key.getBytes());
        String decryptStr = aesUtil.decryptToString(associatedData, nonce, ciphertext);
        System.out.println("decryptStr============"+decryptStr);
        JSONObject decryptStrJson = JSONObject.parseObject(decryptStr);
        //验签
        try {
            boolean isValid = WechatPayUtil.verifySignature(timestamp, nonceStr, body, signature, publicKey);

            if (isValid) {
                JSONObject responseBody = new JSONObject();
                responseBody.put("code", "SUCCESS");
                responseBody.put("message", "OK");
                System.out.println("验签成功");
                //相关业务处理逻辑
               
                return responseBody.toJSONString();
            } else {
                System.err.println("Invalid signature");
                return "{\"code\":\"FAIL\",\"message\":\"Invalid Signature\"}";
            }
        } catch (Exception e) {
            System.err.println("Error verifying signature="+e);
            return "{\"code\":\"FAIL\",\"message\":\"Internal Server Error\"}";
        }
    }
}

2、相实体类

package org.jeecg.modules.business.dispatch.entity;

public class OrderRequest {
    private String outTradeNo;
    private int amount;
    private String openId;

    public String getOpenId() {
        return openId;
    }

    public void setOpenId(String openId) {
        this.openId = openId;
    }

    public String getOutTradeNo() {
        return outTradeNo;
    }

    public void setOutTradeNo(String outTradeNo) {
        this.outTradeNo = outTradeNo;
    }

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }
}

3、相关工具类

        3.1 解密

package org.jeecg.modules.business.dispatch.utils;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesUtil {
  static final int KEY_LENGTH_BYTE = 32;
  static final int TAG_LENGTH_BIT = 128;
  private final byte[] aesKey;
  public AesUtil(byte[] key) {
    if (key.length != KEY_LENGTH_BYTE) {
      throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
    }
      this.aesKey = key;
  }
  public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
  throws GeneralSecurityException, IOException {
    try {
      Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
      SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
      GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
      cipher.init(Cipher.DECRYPT_MODE, key, spec);
      cipher.updateAAD(associatedData);
      return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
    } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
      throw new IllegalStateException(e);
    } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
      throw new IllegalArgumentException(e);
    }
  }
}

        3.2支付相关

package org.jeecg.modules.business.dispatch.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
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.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.jeecg.modules.business.dispatch.entity.OrderRequest;

import java.io.FileReader;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class WechatPayUtil {

    /**
     * 创建下单
     *
     * @param orderRequest
     * @param mchId
     * @param serialNo
     * @param privateKey
     * @param apiUrl
     * @param appId
     * @return
     * @throws Exception
     */
    public static JSONObject createUnifiedOrder(OrderRequest orderRequest, String mchId, String serialNo, PrivateKey privateKey, String apiUrl, String appId,String notifyUrl) throws Exception {
        CloseableHttpClient httpClient = HttpClients.custom().build();

        String url = apiUrl + "/v3/pay/transactions/jsapi";
        HttpPost httpPost = new HttpPost(url);

        // 请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("appid", appId); // 替换为你的小程序AppID
        requestBody.put("mchid", mchId);
        requestBody.put("description", "司机缴费");
        requestBody.put("notify_url", notifyUrl);
        requestBody.put("out_trade_no", orderRequest.getOutTradeNo());
        //TODO 当前费用为测试费用,需要更改成实际费用
        requestBody.put("amount", Map.of("total", 1));
//        requestBody.put("amount", Map.of("total", orderRequest.getAmount()));
        requestBody.put("payer", Map.of("openid", orderRequest.getOpenId())); // 替换为实际的用户openid

        String jsonBody = JSON.toJSONString(requestBody);
        System.out.println("jsonBody==========" + jsonBody);
        StringEntity entity = new StringEntity(jsonBody, StandardCharsets.UTF_8);
        httpPost.setEntity(entity);
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("Accept", "application/json");

        // 签名
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
//        String timestamp = LocalDateTime.now().format(FORMATTER).replace("+0000", "Z");
        String timestamp = new Date().getTime() / 1000 + "";
        System.out.println("timestamp=============" + timestamp);
        String message = "POST" + "\n"
                + "/v3/pay/transactions/jsapi" + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + jsonBody + "\n";

        String signature = sign(message.getBytes(StandardCharsets.UTF_8), privateKey);

        httpPost.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 "
                + "mchid=\"" + mchId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "signature=\"" + signature + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + serialNo + "\"");

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            HttpEntity responseEntity = response.getEntity();
            System.out.println("-------1------");

            if (responseEntity != null) {
                String responseBody = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
                System.out.println("responseBody============" + responseBody);
                String prepayId = JSONObject.parseObject(responseBody).getString("prepay_id");
                JSONObject jsonObject = new JSONObject();
                String payMessage = appId + "\n"
                        + timestamp + "\n"
                        + nonceStr + "\n"
                        + "prepay_id=" + prepayId + "\n";
                String paySignature = sign(payMessage.getBytes(StandardCharsets.UTF_8), privateKey);

                jsonObject.put("prepayId", prepayId);
                jsonObject.put("nonceStr", nonceStr);
                jsonObject.put("signature", paySignature);
                jsonObject.put("signType", "RSA");
                jsonObject.put("timestamp", timestamp);

                return jsonObject;
            }
            System.out.println("-------2------");
        }

        return null;
    }

    /**
     * 验签
     *
     * @param timestamp
     * @param nonceStr
     * @param body
     * @param signature
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static boolean verifySignature(String timestamp, String nonceStr, String body, String signature, PublicKey publicKey) throws Exception {
        // 拼接待签名字符串
        String message = timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";

        // 添加 Bouncy Castle 提供者支持
        Security.addProvider(new BouncyCastleProvider());

        // 初始化 Signature 实例
        Signature signatureInstance = Signature.getInstance("SHA256withRSA", "BC");
        signatureInstance.initVerify(publicKey);
        signatureInstance.update(message.getBytes());
        // 解码签名字符串为字节数组
        byte[] decodedSignature = Base64.getDecoder().decode(signature);
        // 执行验签
        return signatureInstance.verify(decodedSignature);
    }

    /**
     * 获取公钥
     *
     * @param publicKeyPath
     * @return
     * @throws Exception
     */
    public static PublicKey loadPublicKey(String publicKeyPath) throws Exception {
        try (FileReader reader = new FileReader(publicKeyPath);
             PEMParser pemParser = new PEMParser(reader)) {

            Object object = pemParser.readObject();

            if (object instanceof X509CertificateHolder) {
                X509CertificateHolder certHolder = (X509CertificateHolder) object;
                JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
                converter.setProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME);
                java.security.cert.Certificate certificate = converter.getCertificate(certHolder);
                System.out.println("certificate.getPublicKey()========"+certificate.getPublicKey());
                return certificate.getPublicKey();
            } else {
                throw new IllegalArgumentException("Unsupported key type: " + object.getClass().getName());
            }
        }
    }

    public static PublicKey readPEMPublicKey(String pemFilePath) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // 1. 读取 PEM 文件
        FileReader fileReader = new FileReader(pemFilePath);
        PEMParser pemParser = new PEMParser(fileReader);
        Object object = pemParser.readObject();
        pemParser.close();

        // 2. 转换为 PublicKey
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        return converter.getPublicKey((SubjectPublicKeyInfo) object);
    }

    /**
     * 签名
     *
     * @param message
     * @param privateKey
     * @return
     * @throws Exception
     */
    private static String sign(byte[] message, PrivateKey privateKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        java.security.Signature signatureInstance = java.security.Signature.getInstance("SHA256withRSA", "BC");
        signatureInstance.initSign(privateKey);
        signatureInstance.update(message);
        byte[] signedBytes = signatureInstance.sign();
        return Base64.getEncoder().encodeToString(signedBytes);
    }

    /**
     * 获取私钥
     *
     * @param path
     * @return
     * @throws Exception
     */
    public static PrivateKey loadPrivateKey(String path) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        try (FileReader reader = new FileReader(path);
             PEMParser pemParser = new PEMParser(reader)) {

            Object object = pemParser.readObject();
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
            converter.setProvider(BouncyCastleProvider.PROVIDER_NAME);

            if (object instanceof PEMKeyPair) {
                PEMKeyPair pemKeyPair = (PEMKeyPair) object;
                return converter.getPrivateKey(pemKeyPair.getPrivateKeyInfo());
            } else if (object instanceof PrivateKeyInfo) {
                PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) object;
                return converter.getPrivateKey(privateKeyInfo);
            } else {
                throw new IllegalArgumentException("Unsupported key type: " + object.getClass().getName());
            }
        }
    }
}

  文档:JSAPI/小程序下单_小程序支付|微信支付商户文档中心

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

注意要点: 1,Topay里面的参数要填好:appid,appsecret,mch_id,partnerkey,spbill_create_ip 2,openid 需要微信授权获取到 3,每次支付orderNo要不同 openid参考实例: 1,授权链接地址:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxba3445566677&redirect_uri=http://www.acc.com/weixin/pay/paydispatcher&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect 2,转向处理地址:通过第一个链接微信会把code传过来,之前参数获取就行 @RequestMapping(value = "/paydispatcher", method = { RequestMethod.GET }) public void payDispatcher(HttpServletRequest request, HttpServletResponse response) throws Exception { String code = request.getParameter("code"); String msg=""; if(code==null||code.equals("")){ msg="获取微信Code失败!"; request.setAttribute("msg" ,msg); request.getRequestDispatcher("/jsp/login.jsp").forward(request,response); }else{ WeixinUtil util = new WeixinUtil(); UserAccessToken token = (UserAccessToken) request.getSession().getAttribute("UserAccessToken"); if(null==token){ token = util.getAccessToken3(Constants.APPID, Constants.SECRET,code); request.getSession().setAttribute("UserAccessToken",token); } request.setAttribute("openid", token.getOpenid()); request.setAttribute("accessToken", token.getAccessToken()); request.setAttribute("refreshToken", token.getRefreshToken()); request.setAttribute("expiresIn", token.getExpiresIn()); request.getRequestDispatcher("/pay/index.jsp").forward(request,response); } } // 获取用户openid accesstoken public static UserAccessToken getAccessToken3(String appid , String appsecret,String code) { UserAccessToken accessToken = null; String requestUrl = Constants.GET_OPENID_ACCESSTOKEN_URL.replace("APPID" , appid).replace("APPSECRET" , appsecret).replace("CODE" , code); String json = httpRequest(requestUrl , "GET" , null); JSONObject jsonObject = JSONObject.fromObject(json); // 如果请求成功 if (null != jsonObject) { try { accessToken = new UserAccessToken(); accessToken.setAccessToken(jsonObject.getString("access_token")); accessToken.setRefreshToken(jsonObject.getString("refresh_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); accessToken.setOpenid(jsonObject.getString("openid")); accessToken.setScope(jsonObject.getString("scope")); } catch (Exception e) { accessToken = null; // 获取token失败 System.out.println("获取token失败 errcode:{} errmsg:{}"); } } return accessToken; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值