Java开发笔记-小程序微信支付接入

news2024/11/18 8:25:22
步骤:

1.注册微信商户,开通小程序支付业务,获得必要接入参数。(Certificate、PrivateKey、merchantId、SerialNumbe、apiV3Key)

2.微信商户号关联小程序(需目标小程序审核)

3.java使用接入参数发起下单,获取下单参数。

4.小程序使用下单参数,调用微信支付,拉起微信支付。

5.客户支付完成后,java后台接收微信支付回调,完成最后的业务。

6.在商户平台下载微信支付账单与系统订单进行对账。

一、微信商户注册
注册地址

微信商户注册地址

通过上传企业信息,法人信息等,成为商户

开通小程序支付业务

产品中心--我的产品--JSAPI支付

merchantId获取

账户中心-商户信息-微信支付商户号

其他参数获取

Certificate、PrivateKey、SerialNumbe、apiV3Key参数获取:账户中心-API安全。具体步骤,在申请的时候官方会提醒的。

二、关联小程序

产品中心--AppID账号管理--关联APPID

三、发起下单

java使用接入参数发起下单,直接上代码

package com.ancun.netsign;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;


public class Test {

    private static final String appId="";
    private static final String merchantId="";
    private static final String merchantSerialNumber="";
    private static final String apiV3Key="";
    private static final String merchantPrivateKey="";

    public static void main(String[] args) throws Exception {
        payNomal();
        searchOrder();
    }


    public static void payNomal() throws Exception {

        PrivateKey privateKey = PemUtil.loadPrivateKey(
                new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
        PrivateKeySigner signer=new PrivateKeySigner(merchantSerialNumber, privateKey);
        byte[] bs=apiV3Key.getBytes(StandardCharsets.UTF_8);
        WechatPay2Credentials cred=new WechatPay2Credentials(merchantId, signer);
        ScheduledUpdateCertificatesVerifier   verifier = new ScheduledUpdateCertificatesVerifier(cred, bs);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create();
        builder.withMerchant(merchantId, merchantSerialNumber, privateKey);
        builder.withValidator(new WechatPay2Validator(verifier));
        CloseableHttpClient httpClient = builder.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("mchid",merchantId)
                .put("appid", appId)
                .put("description", "测试公司")
                .put("notify_url", "http://回调地址")
                .put("attach","自定义参数")
                .put("out_trade_no", "订单id");
        rootNode.putObject("amount")
                .put("total", 3);
        rootNode.putObject("payer")
                .put("openid", "微信openid");

        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        JSONObject jo=JSONObject.parseObject(bodyAsString);
        System.out.println("prepay_id = " + jo.getString("prepay_id"));

    }



    public static void searchOrder() throws Exception {

        PrivateKey privateKey = PemUtil.loadPrivateKey(
                new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
        PrivateKeySigner signer=new PrivateKeySigner(merchantSerialNumber, privateKey);
        byte[] bs=apiV3Key.getBytes(StandardCharsets.UTF_8);
        WechatPay2Credentials cred=new WechatPay2Credentials(merchantId, signer);
        ScheduledUpdateCertificatesVerifier   verifier = new ScheduledUpdateCertificatesVerifier(cred, bs);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create();
        builder.withMerchant(merchantId, merchantSerialNumber, privateKey);
        builder.withValidator(new WechatPay2Validator(verifier));
        CloseableHttpClient httpClient = builder.build();
        HttpGet httpget = new HttpGet("https://api.mch.weixin.qq.com/v3/pay/transactions/id/交易号?mchid=");
        httpget.addHeader("Accept", "application/json");
        httpget.addHeader("Content-type","application/json; charset=utf-8");
        CloseableHttpResponse response = httpClient.execute(httpget);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);
        JSONObject jo=JSONObject.parseObject(bodyAsString);
        System.out.println(jo);

    }

}
下单接口返回:
{"prepay_id":"wx2310065247629669c55d9ec38dxxxxxx"}
//组装小程序需要的参数:packageInfo、NonceStr、AppId、PaySign、TimeStamp

        String NonceStr= RandomUtil.enUuId();
        Long timeStamp = System.currentTimeMillis() / 1000;
        String packageInfo = "prepay_id=" + prepayId;
        CreateSign02 sign = new CreateSign02();
        String paySign = sign.getToken(appId, packageInfo, NonceStr, timeStamp, merchantPrivateKey);
        String signType="RSA"

package jnpf.utils;



import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;

public class CreateSign02 {

  public  String getToken(String appid,String prepay_id,String nonceStr,long timestamp,String privateKey) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
        //从下往上依次生成
        String message = buildMessage(appid, timestamp, nonceStr, prepay_id);
        //签名
        String signature = sign(message.getBytes("utf-8"),privateKey);
        return  signature;
    }

    String sign(byte[] message,String privateKey) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
        //签名方式
        Signature sign = Signature.getInstance("SHA256withRSA");
        //私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
        sign.initSign(MyPrivatekey.getPrivateKey(privateKey));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

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

        return appid + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + prepay_id + "\n";
    }

}
四、小程序拉起支付
//小程序拉起支付方法
uni.requestPayment({
		provider: 'wxpay',
		timeStamp: timeStamp,
		nonceStr: nonceStr,
		package: package,
		signType: signType,
		paySign: paySign,
		success: res => {
		//成功后的操作
            
		},fail: err => {
				
		},complete: () => {
		}
	})

效果

五、接收回调

业务代码就不贴了,直接上验签和解密代码

 private String checkSignAndDecrypt(HttpServletRequest request) throws Exception {
        StringBuffer xmlStr = new StringBuffer();
        BufferedReader reader = request.getReader();
        String line;
        while ((line = reader.readLine()) != null) {
            xmlStr.append(line);
        }
        //支付回调通知:
        // {
        // "id":"ff9ce7d0-6d60-520a-bb79-ba98aadaef58",
        // "create_time":"2022-01-26T10:07:46+08:00",
        // "resource_type":"encrypt-resource",
        // "event_type":"TRANSACTION.SUCCESS",
        // "summary":"支付成功",
        // "resource":{
        // "original_type":"transaction",
        // "algorithm":"AEAD_AES_256_GCM",
        // "ciphertext":"bHsdM/qkwb+dU0aani3s0TO+HzD4W0AbQ1TyOBL4VFKDN2IEJx9FPFWWpAwywi/5llfPkf4DoyMMc6KDSkVf3U1bk1y6rKcC+XTFg6jPpsMj/H9kqmrLTYXohtJ6PtmUijnJyEKtyjr7Z6scMEY0oRAAOMZlz3IxheXwNc1AO14zUohS3jppF7wS8lgasVTRiGnk8WRzGLyJTH7lo0bUKYtL2Asq9ARESZbDzJAUcfl8ywZTVFGIlItTXte4CT529cRl+oQtbOFXhlW4kRM7k1QVFhQ3I5FOX58+oPf5aOMaUTzh5HMbpxOAHNB6Yr0JR0QmX1qMwRbeaS45aWhd8BBYJPRFk2MuPalAK3oWqHHYTwa9lEHEJHofSRMZDdUVD3gCCEyzDJpeR0bmQlm1oxnkV4CzBQQeoPg/7Dn2VBUjyX2kKpdVMSdiCb7h1HGesl1BuFurcbZ6rayMfX1Nbq9a7C+oofGauKtdAY1tfQOkee2+NfWTKTc3cfwrWB90aYV2uV9G3tCXeD/XRS2VkXsXiU2UljeVnI1Qv7+9eHWtQDpboQ8q2G2pc4MalhXV2g==",
        // "associated_data":"transaction",
        // "nonce":"EzVTGRqlF6GM"
        // }
        // }
        log.info("支付回调通知接收的body参数:" + xmlStr);
        try {
            log.info("开始验签===========================");
            // 验签
            String Signature = request.getHeader("Wechatpay-Signature");
            String Serial = request.getHeader("Wechatpay-Serial");
            String Timestamp = request.getHeader("Wechatpay-Timestamp");
            String Nonce = request.getHeader("Wechatpay-Nonce");

            PrivateKey privateKey = PemUtil.loadPrivateKey(
                    new ByteArrayInputStream(pro.getMerchantPrivateKey().getBytes("utf-8")));
            //加载官方自动更新证书
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    //商户平台查看                            //不是API密钥
                    new WechatPay2Credentials(pro.getMerchantId(), new PrivateKeySigner(pro.getMerchantSerialNumbe(), privateKey)), pro.getApiV3Key().getBytes("utf-8"));

            String s1 = xmlStr.toString();
            //按照文档要求拼接验签串
            String VerifySignature = Timestamp + "\n" + Nonce + "\n" + s1 + "\n";
            log.info("拼接后的验签串=" + VerifySignature);

            //使用官方验签工具进行验签
            boolean verify1 = verifier.verify(Serial, VerifySignature.getBytes(), Signature);
            log.info("验签结果=" + verify1);
            if (!verify1) {
                return "验签失败";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        log.info("开始解密=========================");
        //解密
        JSONObject jo = JSONObject.parseObject(xmlStr.toString());
        JSONObject jodata = jo.getJSONObject("resource");
        String associated_data = jodata.getString("associated_data");
        String nonce = jodata.getString("nonce");
        String ciphertext = jodata.getString("ciphertext");
        AesUtil aesUtil = new AesUtil(pro.getApiV3Key().getBytes());
        String aes = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
        log.info("解密结果=========================" + aes);

        return aes;
    }

回调方法定义:

  /**
     * 微信支付回调
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @PostMapping(value = "/wxPayNotify")
    public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("进入微信支付回调");
        String aes="";
        try {
            WeCharPayProperties pro = new WeCharPayProperties();

             aes = this.checkSignAndDecrypt(pro, request);
            log.info("解密后参数:" + aes);
            if (aes.equals("验签失败")) {
                return "{" +
                        '"' + "code" + '"' + ":" + '"' + "FAIL" + '"' + "," +
                        '"' + "message" + '"' + ":" + '"' + "失败,验签失败" + '"' +
                        "}";
            }
            //=============解析参数开始=======================================

            省略代码

            //============解析参数结束=======================================
            业务代码

        } catch (Exception e) {
            log.error("微信支付回调出错", e);

        }

        log.info("微信支付回调完成====================================================");

        return "{" +
                '"' + "code" + '"' + ":" + '"' + "SUCCESS" + '"' + "," +
                '"' + "message" + '"' + ":" + '"' + "成功" + '"' +
                "}";
    }

至此完成开发,后续的对账就不贴了。根据自己的业务进行对账。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2066188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新手设计师看一看!2024年PDF转CAD软件TOP4

在现在这个啥都能数字化的时代,文件格式得来回换已经成了我们工作的日常,特别是把PDF转成CAD,对工程师、设计师还有搞建筑的人来说,这事儿特别重要。技术越发展,市面上就出现了好些又快又方便的工具。今天,…

AI+新质生产力,化敌为友的新未来

“新质生产力”是今年的“高频词”。如何将“新质生产力”落到实处,业内纷纷瞄准了同一个关键词:“人工智能”,特别是 “人工智能” ,这个小小的""号代表着无限的可能性。人工智能正在成为现代社会的基础设施&#xff0…

便签内容转移新设备攻略

在桌面使用便签软件记录工作和生活中的点点滴滴,已成为许多人的习惯。它不仅能帮助我们捕捉灵感、记录待办事项,还能提醒我们重要的日程安排。然而,随着设备的更新换代或工作地点的变更,我们有时需要将便签内容从一个设备转移到另…

多门店多端平台系统小程序源码

🔥【健身新纪元】多门店多端分开健身系统,打造你的个性化健身体验🌟 🏋️‍♀️ 一、告别单一,拥抱多元化健身时代 你还在为找不到合适的健身房而烦恼吗?或是想要随时随地开启健身模式,却受限…

聚水潭ERP集成用友NC(用友NC主供应链)

源系统成集云目标系统 用友NC介绍 用友NC是用友NC产品的全新系列,是面向集团企业的世界级高端管理软件。它以“全球化集团管控、行业化解决方案、全程化电子商务、平台化应用集成”的管理业务理念而设计,采用J2EE架构和先进开放的集团级开发平台UAP&am…

UDS 诊断 - ReadDataByPeriodicIdentifier(按周期性标识符读取数据)(0x2A)服务

UDS 诊断服务系列文章目录 诊断和通信管理功能单元 UDS 诊断 - DiagnosticSessionControl(诊断会话控制)(0x10)服务 UDS 诊断 - ECUReset(ECU重置)(0x11)服务 UDS 诊断 - SecurityA…

5个网络监控神器,别说老网工藏私不发给你

前言 是2022年度的精选网络工具安利贴,你都看了没? 现在啊,很多企业公司都有不少的网络硬件,而这些网络硬件设备根据不同的功能,会分散在不同的物理位置上。 可能有些网络硬件设备是集中式管理,有些只能…

2024年掏耳勺买什么样的好?四款优良产品推荐,不可错过

21世纪的到来,全民生活品质得到了飞跃进步,大家逐渐开始关注耳道的健康清洁。传统挖耳勺因直径大、材质硬、无法看清耳道等因素,往往很难满足大部分人群的清洁需求,甚至带来不可逆转的伤害。可视挖耳勺的出现,具备可视…

C++第四十弹---从零开始:模拟实现C++中的unordered_set与unordered_map

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1 哈希概念 2 哈希冲突 3 哈希函数 4 哈希冲突解决 4.1 闭散列 4.1.1. 线性探测 4.1.2. 二次探测 4.2 开散列 4.2.1. 开散列概念 4.2.2…

安卓逆向百例十-币coin

typora-root-url: ./pic安卓逆向百例十-币coin 现在售价依旧是99¥,计划更新100案例,平均一个案例1块钱,要什么自行车! 案例起源: 有位老哥 经过炒币失败后想做一个这种查询接口的app,但是苦于他的资金比…

windows系统搭建WSUS更新服务问题整理

1、连接微软更新服务器时,总是连接失败,采取了指定TLS1.2连接的方法,还是连接失败。 解决方法:当时服务器的操作系统为windows server 2012 R2,将操作系统换成windows server 2016可以解决这个问题。 2、客户端怎么配…

Linux发送邮件:如何配置SMTP服务器发信?

linux发送邮件至多个收件人的方法?如何用Linux命令? 在Linux系统中,邮件发送是一个常见且重要的功能,无论是用于系统监控通知还是日常通信。AokSend将详细介绍如何在Linux环境下配置SMTP服务器,以确保您的邮件发送既高…

探索RAG与Multi-Agent的结合:解决复杂任务的新方法

最近帮企业定制了一个langgraphrag的项目,跟大家简单介绍一下设计架构和具体的应用。如果大家有兴趣,我也可以出一期视频,给大家详细介绍一下。我们会一步步探讨如何构建一个可以控的Agent,以执行RAG任务,并最终展示一…

C#为复杂属性提供下拉式编辑框和弹出式编辑框

一.为属性提供编辑类 弹出式和下拉式是如何实现的呢,这需要为属性提供一个专门的编辑类。.Net为我们提供了一个System.Drawing.Design.UITypeEditor类,它是所有编辑类的基类,从他继承出了诸如ColorEditor、FontEditor的类,因此我们…

信刻光盘摆渡系统安全合规实现跨网数据单向导入/导出

在当今信息化、数字化时代,各种数据传输和储存技术发展迅速,各安全领域行业对跨网数据交互需求日益迫切,数据传输的安全可靠性对于整个过程的重要性不可忽视。应如何解决网络安全与效率之间的矛盾,如何安全合规地实现跨网数据单向…

Spring Cloud Gateway 之动态路由

前言 熟悉 Spring Cloud Gateway 的人都知道 Gateway 提供了鉴权、路由等功能,本篇我们重点分析的是 Gateway 的动态路由功能。 Gateway Actuator API 方法源码解析 Spring Cloud Gateway 的 Actuator 端点允许监视 Spring Cloud Gateway 应用程序并与之交互&…

k8s学习(三十八) 使用OpenTelemetry+jaeger实现链路追踪

文章目录 前言一、安装jaeger二、安装cert-manager三、安装OpenTelemetry Operator四、配置 OpenTelemetry Collector五、配置 Instrumentation六、编写java示例程序并测试调用链跟踪 前言 OpenTelemetry 可以用于从应用程序收集数据。它是一组工具、API 和 SDK 集合&#xff…

《黑神话:悟空》Steam全球评价出炉:18个语言区好评率超90%

《黑神话:悟空》Steam在线人数已经不断打破纪录,连续三天刷榜,目前最高成绩超过241万。这个成绩也稳坐总榜第二,同时也是单机游戏的历史第一。 除了游玩人数之外,该作的评价口碑也非常出色,根据媒体汇总的数…

Flutter->`Flutter` 通过`ffi`调用`Rust`编译生成的产物.so文件(Android)和.a文件(iOS)接口方法

flutter_rust_ffi Flutter 通过ffi调用Rust编译生成的产物.so文件(Android)和.a文件(iOS)接口方法; 拾用本文您将获取以下技能: Rust编译.so文件的能力;Rust编译.a文件的能力;Flutter调用.so文件的能力;Flutter调用.a文件的能力; 附加Buff: Flutter环境安装指南;Rust环境安…

游戏行业如此竞争激烈,个人开发者是否仍存机会?

在当今这个数字化时代,游戏行业以其庞大的市场规模、高速的增长速度以及无限的创意空间,吸引了无数开发者投身其中。然而,随着技术的进步、资本的涌入以及大型游戏公司的强势扩张,游戏行业的竞争日益激烈,似乎形成了一…