JAVA服务器端接入微信APP支付记录

2023-05-16

微信开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1

业务流程图如下:

商户系统和微信支付系统主要交互说明:

步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。

步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。

步骤3:统一下单接口返回的正常prepay_id,再按签名规范重新签名生成签名后,将数据传输给APP。参与签名的字段为appid,partnerid,prepayid,noncestr,timestamp,package。注意package格式为Sign=WXPay。

步骤4:商户APP调起微信支付。

步骤5:商户后台接收支付通知。

步骤6:商户后台查询支付结果。

 

统一下单:(接口连接https://api.mch.weixin.qq.com/pay/unifiedorder)

准备的参数有 :

appid 公众账号ID 微信支付分配的公众账号ID(企业号corpid即为此appId)

mch_id 商户号 微信支付分配的商户号

nonce_str 随机字符串 

sign 签名 

body 商品描述

out_trade_no 商户订单号 商户系统内部订单号

total_fee 标价金额 订单总金额,单位为分

spbill_create_ip 终端IP

notify_url 通知地址(回调地址)

trade_type 交易类型 

一共10个必须参数

步骤1:将必填参数放入Map集合中,生成带sign(生成签名的key是在支付平台设置的)的xml格式的字符串,发送给统一下单链接。

步骤2:将微信返回的xml格式的字符串转换成map格式,并校验sign值是否正确。

步骤3:从map中取出returnCode和resultCode,当两个值都是SUCCESS时,再从map中获取prepay_id预支付交易会话标识,

nonce_str随机字符串,并生成一个时间戳time,将这三个参数用之前生成签名的key再生成一个sign,这个是返回客户端用于拉去订单界面的。

代码如下:

Map<String,String> orderMap = new Hash<String,String>();
orderMap.put("appid", WeiXConfig.WEIAPPID);
orderMap.put("mch_id", WeiXConfig.MCH_ID);
orderMap.put("nonce_str", WXPayUtil.generateNonceStr());
orderMap.put("body", WeiXConfig.BODY);
orderMap.put("out_trade_no", model.getId());
Integer payMoney = new BigDecimal(Integer.parseInt(model.getMoney())).multiply(newBigDecimal(100)).intValue(); orderMap.put("total_fee", String.valueOf(payMoney));
orderMap.put("total_fee", String.valueOf(payMoney));
orderMap.put("spbill_create_ip", spbill_create_ip);
orderMap.put("notify_url", WeiXConfig.WEI_NOTIFY_URL);
orderMap.put("trade_type", "APP");
String xmlInfo = WXPayUtil.generateSignedXml(orderMap, WeiXConfig.key);
String weixinPost = HttpsRequestUtil.httpsRequest(uri, "POST", xmlInfo).toString();
Map<String, String> returnMap = WXPayUtil.xmlToMap(weixinPost);
if (WXPayUtil.isSignatureValid(returnMap, WeiXConfig.key)) {
	 String returnCode = returnMap.get("return_code");// 通信标识
	 String resultCode = returnMap.get("result_code");// 交易标识
	 if (returnCode.equals(WXPayConstants.SUCCESS) &&         
           resultCode.equals(WXPayConstants.SUCCESS)) {
		  resultMap.put("code", ConstantUtil.SUCCESS);
		  resultMap.put("desc", ConstantUtil.SUCCESS_DESC);
		  String prepayid = returnMap.get("prepay_id");// 预支付交易会话标识
		  String noncestr = returnMap.get("nonce_str");// 微信返回的随机字符串
		  long time = WXPayUtil.getCurrentTimestamp();// 时间戳/单位秒
		  resultMap.put("prepay_id", prepayid);
		  resultMap.put("nonce_str", noncestr);
		  resultMap.put("mch_id", WeiXConfig.MCH_ID);
		  resultMap.put("timestamp", time);
		  String sign = secondSign(noncestr, time, prepayid, WeiXConfig.key);
		  resultMap.put("sign", sign);

		} else {
		  resultMap.put("code", ConstantUtil.SYS_ERROR);
		  resultMap.put("desc", returnMap.get("return_msg"));
		}
 } else {
	
 }

二次签名代码:

/**
	 * 二次签名
	 * 
	 * @param map 
	 * @param key
	 * @return
	 */
	public String secondSign(String nonce_str, long time, String prepayid, String key) {
		String sign = "";
		try {
			Map<String, String> map = new HashMap<>();
			map.put("appid", WeiXConfig.WEIAPPID);
			map.put("partnerid", WeiXConfig.MCH_ID);
			map.put("package", "Sign=WXPay");
			map.put("noncestr", nonce_str);
			map.put("timestamp", String.valueOf(time));
			map.put("prepayid", prepayid);
			String xmlStr = WXPayUtil.generateSignedXml(map, key);
			LogUtil.info("key-->{}", key);
			Map<String, String> xmlMap = WXPayUtil.xmlToMap(xmlStr);
			LogUtil.info("xmlMap-->{}", xmlMap);
			sign = xmlMap.get("sign");
		} catch (Exception e) {
			LogUtil.info("Exception-->{}", e);
		}
		return sign;
	}

 

到此统一下单结束

查询订单:(链接https://api.mch.weixin.qq.com/pay/orderquery)

需要参数

appid 公众账号ID

mch_id 商户号

out_trade_no 商户订单号

nonce_str  随机字符串

sign  签名

一共5个参数

步骤1:将必填参数放入Map集合中,生成带sign(生成签名的key是在支付平台设置的)的xml格式的字符串,发送给查询订单链接。

步骤2:将微信返回的xml格式的字符串转换成map格式,并校验sign值是否正确。

步骤3:从map中取出appid和mch_id和result_code,当appid、商户mch_id和服务器值相同,并且result_code等于SUCCESS时,取map中trade_state(订单状态),根据订单状态决定是否下一步,订单状态有以下可能:

SUCCESS—支付成功

REFUND—转入退款

NOTPAY—未支付

CLOSED—已关闭

REVOKED—已撤销(刷卡支付)

USERPAYING--用户支付中

PAYERROR--支付失败(其他原因,如银行返回失败)

当订单状态是成功时,核对订单号以及金额是否正确,之后根据订单状态给用户发道具;到此查询订单结束,代码如下:

Map<String,String> orderMap = new Hash<String,String>();
orderMap.put("appid", WeiXConfig.WEIAPPID);
orderMap.put("mch_id", WeiXConfig.MCH_ID);
orderMap.put("out_trade_no", id);
String nonce_str = WXPayUtil.generateNonceStr();
orderMap.put("nonce_str", nonce_str);
String xmlInfo = WXPayUtil.generateSignedXml(orderMap, WeiXConfig.key);
String weixinPost = HttpsRequestUtil.httpsRequest(uri, "POST", xmlInfo).toString();
Map<String, String> returnMap = WXPayUtil.xmlToMap(weixinPost);
String return_code = returnMap.get("return_code");
if (WXPayConstants.SUCCESS.equals(return_code)) {
	String appid = returnMap.get("appid");
	String mch_id = returnMap.get("mch_id");
	String result_code = returnMap.get("result_code");
    /* 验签 */
	if (WXPayUtil.isSignatureValid(returnMap, WeiXConfig.key)) {

		if (WeiXConfig.WEIAPPID.equals(appid) && WeiXConfig.MCH_ID.equals(mch_id)
				&& "SUCCESS".equals(result_code)) {
			String trade_state = returnMap.get("trade_state");// 交易状态
			String total_fee = returnMap.get("total_fee");// 总金额
			String out_trade_no = returnMap.get("out_trade_no");// 商户订单号
			String trade_state_desc = returnMap.get("trade_state_desc");// 交易状态描述
            /* 状态成功 金额正确 */
			if ("SUCCESS".equals(trade_state) && total_fee.equals(String.valueOf(Integer.parseInt(model.getMoney()) * 100))) {
				/* 订单号相同 */
				if (out_trade_no.equals(id)) {
				
				
				}
            }
        }
    }
}

回调结果(连接是自己填的notify_url)

 

代码如下:

		BufferedReader reader = null;
		LogUtil.info(System.currentTimeMillis() + "<收到支付回调>");
		reader = request.getReader();
		String line = "";
		String xmlString = null;
		StringBuffer inputString = new StringBuffer();

		while ((line = reader.readLine()) != null) {
			inputString.append(line);
		}
		xmlString = inputString.toString();
		request.getReader().close();

		if ("".equals(xmlString)) {
			return "";
		}

		Map<String, String> map = WXPayUtil.xmlToMap(xmlString);
		LogUtil.info("----接收到的数据如下:---" + map.toString());
		String out_trade_no = map.get("out_trade_no");

		boolean flag = false;
		String test = Configuration.getProperty(Configuration.TESTINSTANCE);
		/* 不是测试 */
		if (null == test || "".equals(test)) {
			flag = checkWeiPayResult(map);
		} else {
			switch (test) {
			case "2":
				flag = checkWeiPayResult2(map);
				break;
			default:
				flag = checkWeiPayResult1(map);// 默认
				break;
			}
		}

		/* 检验签名 */
		if (flag) {
			Jedis jedis = JedisUtil.getJedis();
			try {
				Map<String, String> payMap = new HashMap<>();
				payMap.put("id", out_trade_no);
				PayOrderVerify entity = payService.queryOrderById(payMap);
				if (!entity.getCode().equals(PayConfig.CODE_FINISH) && entity.getStatus().equals(PayConfig.SEND_STATUS_NOT)) {
					entity.setCode(PayConfig.CODE_FINISH);
					payService.updateOrder(PayUtil.convert(entity));

					LogUtil.info("订单号验证成功,订单号:" + out_trade_no + ",玩家id:" + entity.getUserId());

					/* 通知大厅发货 */
					int command = ConstantUtil.G_CONST_CG_PS_PAY_CALLBACK_COMMAND_R;
					sendGoods(command, entity.getUserId(), entity.getpId(), entity.getId());
				}
				
				jedis.del("order" + out_trade_no);
			} catch (Exception e) {
				LogUtil.info("Exception-->{}", e);
			} finally {
				JedisUtil.returnResource(jedis);
			}
		}
		return returnXML(map.get("return_code"));
	}

参数校验代码:

/* 校验微信支付回调结果 正式的 */
	public boolean checkWeiPayResult(Map<String, String> map) {
		String return_code = map.get("return_code");
		try {
			if ("SUCCESS".equals(return_code)) {
				String mch_id = map.get("mch_id");
				if (WeiXConfig.MCH_ID.equals(mch_id)) {
					if (WXPayUtil.isSignatureValid(map, WeiXConfig.key)) {
						String fee_type = map.get("fee_type");
						if ("CNY".equals(fee_type)) {
							String out_trade_no = map.get("out_trade_no");
							Map<String, String> payMap = new HashMap<>();
							payMap.put("id", out_trade_no);
							PayOrderVerify entity = payService.queryOrderById(payMap);
							if (null != entity) {
								String result_code = map.get("result_code");
								if ("SUCCESS".equals(result_code)) {
									int total_fee = Integer.parseInt(map.get("total_fee"));
									if (Integer.parseInt(entity.getMoney()) * 100 == total_fee) {
										return true;
									} else {
										LogUtil.info("总金额不对,错的total_fee-->{},正确的total_fee-->{}", total_fee,
												Integer.parseInt(entity.getMoney()) * 100);
									}
								} else {
									LogUtil.info("result_code-->{}", result_code);
								}
							} else {
								LogUtil.info("订单号不对,out_trade_no-->{}", out_trade_no);
							}
						} else {
							LogUtil.info("货币种类不同,fee_type-->{}", fee_type);
						}
					} else {
						LogUtil.info("签名验证失败,sign-->{}", map.get("sign"));
					}
				} else {
					LogUtil.info("商户ID不一致,mch_id-->{}", mch_id);
				}
			} else {
				LogUtil.info("return_code-->{}", return_code);
			}

		} catch (Exception e) {
			LogUtil.info("Exception-->{}", e);
		}
		return false;
	}

 

到此全部结束!!!

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA服务器端接入微信APP支付记录 的相关文章

  • 哪个类调用了我的静态方法?

    假设我有一个带有静态方法的 Java 类 如下所示 class A static void foo Which class invoked me 进一步假设 A 类有任意数量的子类 class B extends A class C ext
  • 不同的 JDK 更新会产生不同的 Java 字节码吗?

    假设场景 我有一个项目 其源合规性级别指定为 1 5 现在 我使用两种不同的 JDK 编译此项目 首先使用 JDK 6 Update 7 然后使用 JDK 6 Update 20 这两个不同的 JDK 是否会生成不同的 Java 字节代码
  • 在 MongoDB Java 驱动程序中如何使用 $filter

    我有一个适用于 MQL 的查询 我需要将其翻译成Java MQL 中的查询如下所示 db
  • 如何将列表转换为地图?

    最近我和一位同事讨论了转换的最佳方式是什么List to Map在 Java 中 这样做是否有任何具体的好处 我想知道最佳的转换方法 如果有人可以指导我 我将非常感激 这是个好方法吗 List
  • 从 Bitmap 类创建 .bmp 图像文件

    我创建了一个使用套接字的应用程序 客户端在其中接收图像并将图像数据存储在 Bitmap 类中 谁能告诉我如何创建一个名为我的图像 png or 我的图像 bmp来自此 Bitmap 对象 String base64Code dataInpu
  • JPA:如何将字符串持久保存到数据库字段中,输入 MYSQL Text

    需求是用户可以写文章 所以我选择typeText为了contentmysql数据库内的字段 我怎样才能转换Java String into MySQL Text 干得好Jim Tough Entity public class Articl
  • 适用于 Solaris 的 Java 8 中缺少 javaws

    看起来 Oracle 从 Java 8 for Solaris 中删除了 Java Web Start javaws 在 Java 8u51 中不再可用 来自兼容性指南 http www oracle com technetwork jav
  • 我需要显式关闭连接吗?

    我持有一个实例MongoClient and DB在我的应用程序中 每次我想执行某些操作时 我都会调用getCollection 我想知道是否需要显式关闭连接 就像connection close 在 JDBC 中 强调一下 我只有一个Mo
  • Jodatime 日期格式

    是否可以格式化 JodaTime 日期 这是代码 private static LocalDate priorDay LocalDate date1 do date1 date1 plusDays 1 while date1 getDayO
  • 总结二维数组

    鉴于我当前的程序 我希望它在用户输入所有值后计算每列和每行的总和 我当前的代码似乎只是将数组的值加倍 这不是我想要做的 例如 如果用户输入具有以下值 1 2 3 2 3 4 3 4 5 的 3x3 矩阵 则看起来就像我在下面的程序中对其进行
  • Spring 术语中命令、表单、业务和实体对象之间的区别?

    我试图理解这些对象在松散耦合系统方面的差异 业务对象与实体对象相同吗 我可以使用 MVC 中的业务或实体对象作为我的命令对象吗 命令对象与表单对象相同吗 只是寻找 Spring 术语和用法中对象类型的说明 我在 stackoverflow
  • activemq 的优先级

    我们目前正在使用 JMS 和 activemq 5 5 1 开发一个应用程序 我们想为某些消息定义更高的优先级 这将使它们首先被消耗 设置生产者和消费者后 通过spring 3 1 JMSTemplate 优先级并不能完全发挥作用 事实上
  • Java中无参数的for循环

    我在看别人的代码 发现了这段代码 for 我不是 Java 专家 这行代码在做什么 起初 我认为这会创建一个无限循环 但在该程序员使用的同一个类中 while true 其中 如果我错了 请纠正我 是一个无限循环 这两个相同吗 为什么有人会
  • 在 Java 中使用 Inflater 解压缩 gzip 数据

    我正在尝试使用以下方法解压缩 gzip 数据Inflater 根据文档 如果参数 nowrap 为 true 则 ZLIB 标头和校验和 字段将不会被使用 这提供了与 GZIP 和 PKZIP 使用的压缩格式 注意 使用 nowrap 选项
  • java Runtime.getRunTime().exec 和通配符?

    我正在尝试使用删除垃圾文件 Process p Runtime getRuntime exec 只要我不使用通配符 它 就可以正常工作 即 Process p Runtime getRuntime exec bin rm f specifi
  • Storm Spout 未收到 Ack

    我已经开始使用storm 所以我使用创建简单的拓扑本教程 https github com nathanmarz storm wiki Tutorial 当我运行我的拓扑时LocalCluster一切看起来都很好 我的问题是我没有得到元组的
  • Python 可以替代 Java 小程序吗?

    除了制作用于物理模拟 如抛射运动 重力等 的教育性 Java 小程序之外 还有其他选择吗 如果你想让它在浏览器中运行 你可以使用PyJamas http pyjs org 这是一个 Python 到 Javascript 的编译器和工具集
  • 将带有时区的 Joda-Time `DateTime` 转换为没有时区的 DateTime?

    Given a DateTime http www joda org joda time apidocs org joda time DateTime html例如2015 07 09T05 10 00 02 00 using 乔达时间 h
  • Java,如何管理线程读取socket(websocket)?

    我有一个 WebSocket 服务器 我的服务器创建一个新线程来处理新连接 该线程一直处于活动状态 直到 websocket 中断 我的问题 对于 1 000 000 个连接 我需要 1 000 000 个线程 我如何通过一个线程处理多个
  • 线程“main”中出现异常 java.lang.UnsatisfiedLinkError: ... \jzmq.dll: 找不到依赖库

    我有一个使用 ZMQ 的 java 应用程序 我已经能够在我的 Win7 PC 上运行它 我将 jzmq dll 放在 jar 可执行文件所在的同一文件夹中 然后通过命令 java jar myapp jar 运行它 我的下一步是将其移至服

随机推荐

  • 基于PySide6的MySql数据库快照备份与恢复软件

    db camera 软件介绍 db camera是一款MySql数据库备份 xff08 快照保存 xff09 与恢复软件 功能上与dump类似 xff0c 但是提供了相对有好的交互界面 xff0c 能够有效地管理导出的sql文件 使用场景
  • 2023-04-14 Linux连接WiFi常用命令,用命令打开、搜索、连接wifi,在buildroot 嵌入式系统上测试,用 wifi_start.sh WIFI名称 WIFI密码 命令格式连接

    一 ifconfig 命令查看打开的网卡 二 ifconfig a 命令查看所有的网卡 xff0c 包括关闭的 三 打开WIFI命令 xff1a ifconfig wlan0 up xff1b 关闭WIFI命令 xff1a ifconfig
  • 基于ubuntu20.04的anaconda和cuda安装

    文章目录 使用服务器训练深度学习模型环境安装 ubuntu20 04 ubuntu 20 04安装anaconda安装显卡驱动安装cuda安装 xff08 cuda 11 0 cudnn安装 cudnn 11 0 使用服务器训练深度学习模型
  • 创建Web天气插件之vue3.0(包含2.x)

    1 首先选择一个天气插件 xff0c 这里小编使用和风天气插件 xff0c 见和风天气插件 和风天气插件产品 xff0c 免费 跨终端 适配你的网站 APP 公众号 2 小编这里写的是vue3 0的移动端项目 xff0c 所以使用的是天气简
  • MySQL占用CPU过高,排查原因及解决的多种方式法

    一 mysql中的wait timeout坑 mysql gt show variables like 39 timeout 39 首先你要明白 wait timeout 指的是mysql在关闭一个非交互的连接之前所要等待的秒数 xff0c
  • Linux系统Debian安装

    虚拟机VMware安装Debian 小白教程 最近学习中顺便做了个文档 xff0c 下载 一 下载Debian 网址 xff1a https www debian org distrib 二 安装Debian8 1 选择稍后安装操作系统 2
  • sqlite3

    一 版本 从 www sqlite org 网站可下载到最新的 sqlite 代码和编译版本 写此文章时 xff0c 最新代码是 3 3 17 版本 二 基本编译 把 sqlite3 c 和 sqlite3 h 添加到工程 xff0c 再新
  • arduino esp8266开发板下载出错解决方法

    常规 arduino esp8266开发板下载安装流程 xff1a 第一步 xff0c 在arduino界面 xff0c 选择 文件 首选项 xff1a 找到附加开发板管理器网址 xff0c 将下面网址复制进去 xff1a http ard
  • linux C编程4-系统信息/时间/内存分配/随机数/定时器

    目录 1 获取系统信息 1 1 uname 函数 1 2 sysinfo 函数 1 3 gethostname 函数 1 4 sysconf 函数 1 5 getpagesize 函数 2 时间 日期 2 1 查看utc时间 2 2 查看系
  • IntelliJ IDEA插件之下载和安装方式

    IntelliJ IDEA插件下载与安装 下载方式官网下载如下图 IDEA内下载如下图 安装方式直接安装如下图 下载插件到本地安装如下图 下载方式 官网下载 如下图 IDEA内下载 如下图 进入Settings页面 xff08 File g
  • IntelliJ IDEA插件之TranslationPlugin

    IntelliJ IDEA插件之TranslationPlugin 下载地址安装插件概述使用如下图 下载地址 下载地址 安装插件 安装方式 概述 翻译他人代码或者源码中的英文 使用如下图
  • IntelliJ IDEA插件之Power Mode II

    IntelliJ IDEA插件之Power Mode II 下载地址安装插件概述使用如下图 下载地址 下载地址 安装插件 安装方式 概述 Power Mode II是写代码的时候体验狂拽酷炫的效果 使用如下图
  • IntelliJ IDEA插件之Background Image Plus +

    IntelliJ IDEA插件之Background Image Plus 43 下载地址安装插件概述 下载地址 下载地址 安装插件 安装方式 概述 IDEA 背景修改插件
  • IntelliJ IDEA插件之Free MyBatis plugin

    IntelliJ IDEA插件之Free MyBatis plugin 下载地址安装插件概述 下载地址 下载地址 安装插件 安装方式 概述 mapper接口与对应xml文件对应
  • MySQL Workbench 8.0 CE 汉化包下载

    MySQL Workbench 8 0 CE 汉化包下载 下载地址 xff1a 汉化文件下载 下载提示 xff1a 下载过程进度文件页面右键另存为即可保存文件到本地 1 把下载好的文件后放到此目录下替换此文件 C Program Files
  • IntelliJ IDEA插件之CodeGlance

    IntelliJ IDEA插件之CodeGlance 下载插件安装插件概述 下载插件 下载地址 安装插件 安装方式 概述 将类似于Sublime中的代码的微型地图嵌入到编辑器窗格中 使用您自定义的颜色对亮色和深色主题进行语法突出显示
  • IDEA Easy Code使用记录

    下载安装 xff08 忽略 xff09 设置 设置在idea的 Other Settings里面 分4个选项 Type Mapper 类型映射 xff08 数据字段和java包装器映射 xff09 Template Setting 模板设置
  • windows 10 安装MySQL5.7.25(支持多个MySQL实例)

    windows 10 安装MySQL5 7 25 下载安装包解压到对应路径 创建my ini 配置文件 xff0c 内容如下 span class token punctuation span mysql span class token
  • MySQL explain学习(MySQL版本5.7.25)

    MySQL explain学习 xff08 MySQL版本5 7 25 xff09 idselect typetablepartitionstype xff08 重要 xff09 possible keyskeykey lenrefrows
  • JAVA服务器端接入微信APP支付记录

    微信开发文档地址 xff1a https pay weixin qq com wiki doc api app app php chapter 61 8 1 业务流程图如下 xff1a 商户系统和微信支付系统主要交互说明 xff1a 步骤1