微信支付JSAPI的V3版本

JSAPI下单

https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml

准备:

一个微信公众号,一个微信商户号,一套前后台系统

AppSecret:在上面也能看到,是微信公众号里的开发者密码

APPID:指的是公众号的开发者ID

mchid:这个就是传说中的商户号id,商户平台

API证书:商户平台-账户中心-API安全页面

 API秘钥:商家秘钥,功能是解密商家发起访问时用商户证书加密的数据

APIv3秘钥:微信支付平台秘钥,功能是下载平台证书和回调的时候解密平台返回参数用的。

流程:

(1)后台生成订单,调用统一下单接口,拿到返回数据加密给前台

(2)前台拿到数据拉起微信支付

(3)支付成功回调后台接口,后台检验结果完成支付成功后的业务处理,整个流程结束

代码实现:

说明:傻瓜式复制一下三大步代码,完成微信公众号H5支付功能,由于工具类为辅助封装内容,下面代码中个别工具类下载地址如下:

链接:https://pan.baidu.com/s/1ZSIEf3fmAW_Wr9RagcPVAg?pwd=dpn0
提取码:dpn0

统一下单接口调用

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;
 /**   WeixinPayService
     * H5支付
     *
     * @param body:说明语,如XX充值
     * @param out_trade_no:订单号
     * @param total:订单金额,分
     * @return data.prepay_id

       WeChatConstant:常量类,自行建立
     */ 
public String h5wxpay(String body, String out_trade_no, int total,String openid) throws IOException, GeneralSecurityException, NotFoundException, HttpCodeException {
        Verifier verifier = this.verify();
        PrivateKey merchantPrivateKey = getPrivateKey();
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(WeChatConstant.mch_id, WeChatConstant.merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));

        HttpPost httpPost = new HttpPost(WeChatConstant.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("mchid", WeChatConstant.mch_id)
                .put("appid", WeChatConstant.app_id)
                .put("description", body)
                .put("notify_url", WeChatConstant.notify_wurl)
                .put("out_trade_no", out_trade_no);
        rootNode.putObject("amount")
                .put("total", total);

        rootNode.putObject("payer").put("openid", openid);
        objectMapper.writeValue(bos, rootNode);
        CloseableHttpClient httpClient = builder.build();
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);
        return bodyAsString;
    }

 /**
     * 定时更新平台证书功能
     * @return
     * @throws NotFoundException
     * @throws IOException
     * @throws GeneralSecurityException
     * @throws HttpCodeException
     */
    private Verifier verify() throws NotFoundException, IOException, GeneralSecurityException, HttpCodeException {
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        PrivateKey merchantPrivateKey = getPrivateKey();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(WeChatConstant.mch_id, new WechatPay2Credentials(WeChatConstant.mch_id,
                new PrivateKeySigner(WeChatConstant.merchantSerialNumber, merchantPrivateKey)), WeChatConstant.api_key_3.getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号,可继续调用putMerchant添加商户信息

        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(WeChatConstant.mch_id);
        return verifier;
    }

 /**
     * 加载商户私钥
       apiclient_key_2.pem:商户证书
     * @return
     */
    private PrivateKey getPrivateKey() {
        String path = this.getClass().getClassLoader().getResource("").getPath();
        PrivateKey merchantPrivateKey = null;
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(
                    new FileInputStream(path +"/cert/apiclient_key_2.pem"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return merchantPrivateKey;
    }

唤起移动端微信支付

/**唤起移动端微信支付页面接口**/
@GetMapping("/wx/sign")
    public Object wxsign(@RequestParam String prepay_id) {
        //String appId, long timestamp, String nonceStr, String pack
        if(StringUtil.isEmpty(prepay_id)){
            return Rets.failure("prepay_id不允许为空");
        }
        Map token = null;
        try {
             Map token = createSign.getToken(WeChatConstant.app_id, prepayId, WeChatConstant.CERT_URL);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Rets.success(token );

    }
import org.springframework.stereotype.Repository;
import java.io.IOException;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author :wt
 * @date :Created in 2023/5/6 11:10
 * @description:辅助签名类
 * @modified By:
 */
@Repository
public class CreateSign {
    public Map getToken(String appid,String prepay_id, String privateKeyPath) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
        String randomOnce = RandomStringGenerator.getRandomStringByLength(32);
        //随机字符串
        String nonceStr = randomOnce;//真!随机字符串
        //时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        prepay_id = "prepay_id="+prepay_id;
        //从下往上依次生成
        String message = buildMessage(appid, timestamp, nonceStr, prepay_id);
        String signature = sign(message.getBytes("utf-8"), privateKeyPath);
        Map param = new HashMap<>();
        param.put("appId",appid);
        param.put("timeStamp",String.valueOf(timestamp));
        param.put("nonceStr",randomOnce);
        param.put("package",prepay_id);
        param.put("signType","RSA");
        param.put("paySign",signature);
        return  param ;
    }
    public String sign(byte[] message, String privateKeyPath) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
        //签名方式
        Signature sign = Signature.getInstance("SHA256withRSA");
        //在本地环境运行使用 私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
        sign.initSign(MyPrivatekey.getPrivateKey(privateKeyPath));
        //在服务器中使用这种方式
//        FileReader fileReader = new FileReader(privateKeyPath);
//        sign.initSign(MyPrivatekey.getPrivateKey(fileReader.readString()));
        sign.update(message);

        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     *  按照前端签名文档规范进行排序,\n是换行
     * @param appid
     * @param timestamp
     * @param nonceStr
     * @param packag
     * @return
     */
    public String buildMessage(String appid, long timestamp,String nonceStr,String packag ) {
        return appid + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                +packag  + "\n";
    }
}


public class RandomStringGenerator {
	/**
     * 获取一定长度的随机字符串
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}

回调后台接口,处理业务逻辑

/**
     * 移动微信支付回调
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "wxin/notify",method = RequestMethod.POST)
    public Object wxinNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String reqParams = StreamUtils.read(request.getInputStream());
        logger.info("微信回调-------支付结果:"+reqParams);
        JSONObject jsonObject = JSONObject.parseObject(reqParams);
        String eventType = (String) jsonObject.get("event_type");
        JSONObject resource = (JSONObject) jsonObject.get("resource");
        String associated_data = (String) resource.get("associated_data");
        String nonce = (String) resource.get("nonce");
        String ciphertext = (String) resource.get("ciphertext");

            //如果微信返回状态为TRANSACTION.SUCCESS
            if (eventType.equals("TRANSACTION.SUCCESS")) {
                response.setStatus(200);
                try {

                   //业务处理
                    String result = new AesUtil(WeChatConstant.api_key_3.getBytes()).decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
                    logger.info("微信支付回调解密结果:" + result);

                    JSONObject jsonObject1 = JSONObject.parseObject(result);
                    String orderSn = jsonObject1.getString("out_trade_no");
                    Order order = orderService.getByOrderSn(orderSn);
                    if (order == null) {
                        return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn);
                    }
                    // 检查这个订单是否已经处理过
                    if (order.hasPayed()) {
                        return WxPayNotifyResponse.success("订单已经处理成功!");
                    }

                   // order.setPayId(payId);

                   //支付成功,修改订单状态OrderEnum.PayTypeEnum.UN_SEND为已支付
                    orderService.paySuccess(order, OrderEnum.PayTypeEnum.UN_SEND.getKey());
                   //业务处理结束
                } catch (Exception e) {
                    throw new RuntimeException("更新付款状态失败!");
                }
            }else{
                return WxPayNotifyResponse.fail("支付失败");
            }


        return WxPayNotifyResponse.success("支付成功");
    }
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
 * @author :wt
 * @date :Created in 2023/5/11 10:17
 * @description:
 * @modified By:
 */
public class StreamUtils {
    public static String read(InputStream is){
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int len = 0;
            byte[] buffer = new byte[512];
            while((len = is.read(buffer)) != -1){
                baos.write(buffer, 0, len);
            }
            return new String(baos.toByteArray(), 0, baos.size(), "utf-8");
        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";
    }
}
1、所有文章未经授权禁止转载、摘编、复制或建立镜像,如有违反,追究法律责任。
2、本站文章部分来源注册用户发布或互联网收集而来,若有侵权,请邮件联系作者。
邮箱地址:wtao219@qq.com
THE END
分享
二维码
< <上一篇
下一篇>>