java微信支付V3(JSAPI支付)前后端代码(uniapp)

直接复制粘贴即可

java端使用的是springboot,前端使用的是uniapp。使用环境是公众号H5调用微信支付(JSAPI)。符合条件的可以直接复制粘贴。(按流程粘贴出来的代码)(最后有整体的代码)

按流程出来的代码

  1. (uniapp端)在前端访问后台接口

前端正常访问接口,传入参数有你商品的信息
transactionsH5Pay().then(res => {
在这接收参数,在第三步有体现
})

  1. java端 -第一步访问的接口

POM文件引入maven依赖

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

uniapp端第一步访问的接口

    @PostMapping("/gwc/weChat/pay/transactions/h5")
    public AjaxResult weChatPayH5(HttpServletRequest res){
//    这是我的 根据token获取用户信息-可忽略
        WxLoginUser wxUser = wxTokenService.getWxUser(res);
        // 构建service
        service = new JsapiService.Builder().config(config).build();
        // request.setXxx(val)设置所需参数,具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        //下边的参数都是文档有的,金额,appid等,我写的静态的
        amount.setTotal(100);
        request.setAmount(amount);
        request.setAppid(appid);
        request.setMchid(merchantId);
        request.setDescription("测试商品标题");
        request.setNotifyUrl("回调地址");
        request.setOutTradeNo("订单号");
        Payer payer = new Payer();
        payer.setOpenid("付钱人的openid");
        request.setPayer(payer);
        PrepayResponse response = service.prepay(request);
        // 这就是JSAPI下单返回的会话标识,需要返回给前段,调支付用
        System.out.println(response.getPrepayId());
        return AjaxResult.success(response.getPrepayId());
    }

//  里边会报错的依赖类我粘贴在这

	import com.wechat.pay.java.service.payments.jsapi.JsapiService;
	import com.wechat.pay.java.core.Config;
	
    private JsapiService service;
	Config config = new RSAAutoCertificateConfig.Builder()
                        .merchantId("merchantId-商户号")
                        .privateKeyFromPath("私钥证书文件的地址")
                        .merchantSerialNumber("序列号-在微信支付里边获取,可以自己搜索一下")
                        .apiV3Key("私钥的key- 这个是自己在微信支付里边设置的")
                        .build();;

关于第二步里边几个参数的说明
appid 和 merchantId 这两个就不多说了。一个是公众号的appid在微信公众平台获取。一个是商户号在微信支付里边获取。
privateKeyPath 是你apiclient_key.pem的地址。
merchantSerialNumber 是序列号
apiV3Key 是证书的key
payNotifyUrl 是支付完后的回调地址-这是你自己的接口地址,用户支付完之后,微信调你的接口(外网需要可以访问到)

找不到的这个可以在网上搜索怎么找!
在这里插入图片描述

  1. uniapp端-接收参数,并访问后端获取支付所需的参数和签名。
// 第一步的方法
transactionsH5Pay().then(res => {
	//第二步返回的参数
	const prepayId = res.msg;
	getPayConfigParams({
		// 这是你当前页面的路径
		webUrl: window.location.href,
		// 返回的参数
		extString: prepayId
	}).then(res => {
		//后续在这调起支付
	})
})
  1. java端-接收路径参数和会话标识参数。(其中并没有存储,没有判断过期时间,大佬可自行添加,不添加没有影响)
@PostMapping("/pay/config/params")
    private AjaxResult getPayConfigParams(@RequestBody PayUtil payUtil){
    	//接收参数的是实体类,就这两个字段,就不粘贴了
		String webUrl = payUtil.getWebUrl();
        String extString = payUtil.getExtString();
		try{
		//获取access_token
		String grantParam = URLEncoder.encode("client_credential", "UTF-8");
            String appidParam = URLEncoder.encode("你的appid", "UTF-8");
            String secretParam = URLEncoder.encode("你的secret-都在微信公众平台获取", "UTF-8");
            String fileUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+ grantParam +"&appid=" + appidParam + "&secret=" + secretParam;
            URL url = new URL(fileUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            StringBuilder response = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            conn.disconnect();
            JSONObject jsonObject =  JSON.parseObject(response.toString());
            // 获取到access_token
            String accessToken = jsonObject.get("access_token").toString();
            
			// 获取 ticket
			 String ticket= "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ accessToken + "&type=jsapi";
            URL urlTicket = new URL(ticket);
            HttpURLConnection con = (HttpURLConnection) urlTicket.openConnection();
            con.setRequestMethod("GET");
              BufferedReader readerInfo = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String lineInfo;
            StringBuilder responseIn = new StringBuilder();
            while ((lineInfo = readerInfo.readLine()) != null) {
                responseIn.append(lineInfo);
            }
            reader.close();
            con.disconnect();
            JSONObject jsonObjectinfo =  JSON.parseObject(responseIn.toString());
            //  获取到 ticket
            String ticketStr = jsonObjectinfo.get("ticket").toString();
			
			// 随机字符串
			 // 生成随机UUID并去掉其中的横杠
       		 String uuid = UUID.randomUUID().toString().replace("-", "");
			 String randomStr = uuid.substring(0, 32);
			
			// 时间戳
			long currentTimestampMillis = System.currentTimeMillis();
			long timestamp = currentTimestampMillis / 1000

			//生成微信支付的签名
			String generateSignature = generateSignature(appid, timestamp, randomStr,"prepay_id="+extString);
			//返回前端的字段
 			Map<String,Object> map = new HashMap<>();
 			 map.put("nonceStr", randomStr);
            map.put("timestamp", timestamp);
            map.put("signType", "RSA");
            map.put("paySign", generateSignature);
             return AjaxResult.success(map);
          }catch (Exception e){
           	e.printStackTrace();
       	}
       	return AjaxResult.error();
	}
	// 封装的签名方法
	private String generateSignature(String appId, long timestamp, String noncestr, String extString) {
		try {
            // 构建签名串
            StringBuilder signatureStringBuilder = new StringBuilder();
            signatureStringBuilder.append(appId).append("\n");
            signatureStringBuilder.append(timestamp).append("\n");
            signatureStringBuilder.append(noncestr).append("\n");
            signatureStringBuilder.append(extString).append("\n");

            String signatureData = signatureStringBuilder.toString();
            
            Signature signature = Signature.getInstance("SHA256withRSA");
			// 实际的私钥-方法在下边
            PrivateKey privateKey = getPrivateKey();
            
            signature.initSign(privateKey);
            signature.update(signatureData.getBytes());
            byte[] signatureBytes = signature.sign();

            String base64Signature = Base64.getEncoder().encodeToString(signatureBytes);
            return base64Signature;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
	}
	
	 private PrivateKey getPrivateKey() throws Exception {
	 	// 上边对参数的说明的其中 apiclient_key.pem  里边的值
        String privateKeyString = "-----BEGIN PRIVATE KEY-----\n" +
                "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDb4XGTiwe9W+t9\n" +
                ....
                "dJNLl8NmXcRFHOgtk/w8eehrB1lK7LnXihHfSSxV0l43GArYOkJus7OvXjlneEXe\n" +
                "T4NJ2NPqOIwKfo8gRKDfjv6zyRyR2TV8SE5f7wmeeXbJA4da4tAeOtjo562pkmON\n" +
                "naIg+qhkrUuV8OwpjeR5mR4=\n" +
                "-----END PRIVATE KEY-----\n";

        // 去除首尾的多余内容,并生成PrivateKey对象
        privateKeyString = privateKeyString.replaceAll("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll("-----END PRIVATE KEY-----", "").replaceAll("\n", "");
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

}
  1. uniapp端-调起支付
// 第一步的方法
transactionsH5Pay({你的商品信息}).then(res => {
	//第二步返回的参数
	const prepayId = res.msg;
	getPayConfigParams({
		// 这是你当前页面的路径
		webUrl: window.location.href,
		// 返回的参数
		extString: prepayId
	}).then(res => {
		//后续在这调起支付
		//注意:在我开发过程中发现一个问题,安卓可以调用成功,
		//但是苹果手机报错:缺少参数timeStamp。
		//这个问题是timeStamp的类型问题,要求是String类型,但是这里是long类型。
		 WeixinJSBridge.invoke('getBrandWCPayRequest', {
	        "appId": "你的appid-也可以后端直接返回", 
	        "timeStamp": res.data.timestamp,      
	        "nonceStr": res.data.nonceStr,  
	        "package": `prepay_id=${prepayId}`,
	        "signType": "RSA", 
	        "paySign": res.data.paySign
	    },
	    function(resq) {
	        if (resq.err_msg == "get_brand_wcpay_request:ok") {
	            // 使用以上方式判断前端返回,微信团队郑重提示:
	            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
				console.log("支付结果回调:");
				console.log(resq);
	        }
	    });
	})
})

这五步就可以直接调起微信支付(V3-JSAPI支付)

整体代码

uniapp代码— 把第五步的内容 就是前端要写的代码
java代码 — 看着上边的可以直接复制粘贴好吧,勤快一点~~

以上就是调起支付的代码,支付完之后就牵扯到支付的回调

官方文档在这里:微信官方JSAPI支付通知

回调就对应着第二步 payNotifyUrl这个值。说白了就是一个接口地址(不要权限验证)。下边就是代码:


import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;


@PostMapping("/...../...../callback")
    public Map<String, String> payNotifyInfo(@RequestBody JSONObject jsonObject){
        System.out.println("------------------支付回调地址被调用-----------------------");
        try{
        	// 你的秘钥,我是封装到实体中了
            String key = wxPayConfig.getApiV3Key();
            String json = jsonObject.toString();
            String associated_data = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.associated_data");
            String ciphertext = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.ciphertext");
            String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");

			// 其中 AesUtil 这个类,在文章最下边。
            String decryptData = new AesUtil(key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
            //验签
            JSONObject decryptDataObj = JSONObject.parseObject(decryptData, JSONObject.class);
            //解码后的对象--下边的图片有体现
            System.out.println(decryptDataObj);
			//有你的订单号,下边就可以写自己的逻辑-----
			***********
			***********
			***********
        }catch(Exception e) {
            e.printStackTrace();
        }

        Map<String, String> res = new HashMap<>();
        res.put("code", "SUCCESS");
        res.put("message", "成功");
        return res;
    }


	//  其中所需要的maven依赖我写在这
	<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.0</version>
        </dependency>



在这里插入图片描述

少了一个AesUtil,以下是该类的代码,感谢 YLJisKing666666 的提醒:

package com.ruoyi.system.util;

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);
        }
    }
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不是码神(dn)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值