java开发 微信商家转账到零钱,发起商家转账API,微信支付

微信发起商家转账API,以前是企业转账,没有进行微信开发的同学比较头疼,因为微信API太庞大了,慢慢了解下来发现需要看的东西比较多,很多描述的还不是重点,现在记录一下这次转账遇到的问题和整体流程.

第一步需要获取OpenId,获取OpenId需要先拿Core去获取
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=回调域名&response_type=code&scope=snsapi_base&state=STATE

1、域名进入natapp官网下载安装natapp并且申请一个免费隧道或者购买隧道
2、打开cmd进入natapp安装地址,输入natapp -authtoken=(官网获取的token),如果出现以下画面就说明映射成功,此窗口千万千万不能关闭。

 配置回调域名 不需要http和https前缀

 回调地址是域名加上自己的方法名获取到OpenId就可以进行下一步了

    @RequestMapping("/getToken")
    @ResponseBody
    public static void auth(@RequestParam(value="code",defaultValue = "",required = false) String code){

        String appId = "";
        String secret = "";

        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appId+"&secret="+secret+"&code="+code+"&grant_type=authorization_code";
        RestTemplate restTemplate = new RestTemplate();
        String result = restTemplate.getForObject(url, String.class);
        System.out.println("result={}"+result);
        JSONObject json = JSONObject.parseObject(result);
        String openid = json.getString("openid");
    }

发起商家转账API

在这里插入图片描述

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.2</version>
</dependency>

import cn.hutool.core.util.IdUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class WechatPay {
	private static final Gson GSON = new GsonBuilder().create();

	public static void main(String[] args) throws Exception {
	   //测试微信转账
		weixinTransferBat();
	}
	//请看下面
    public static String weixinTransferBat() {...};
	public static String weixinTransferBat() throws Exception {


		//商户号
		String mchid = "";
		//申请商户号的appid或商户号绑定的appid(企业号corpid即为此appid)
		String appId = "";
		//用户在直连商户应用下的用户标示
		String openId = "";
		//商户证书编号
		String wechatPayserialNo = "";
		//商户证书路径(在你本机测试时放你本机路径中的就可以)
		String privatekeypath = "";


		Map<String, Object> postMap = new HashMap<String, Object>();

		//商家批次单号 长度 1~32
		String outNo = IdUtil.getSnowflake(0, 0).nextIdStr();

		postMap.put("appid", appId);
		postMap.put("out_batch_no", outNo);
		//该笔批量转账的名称
		postMap.put("batch_name", "测试转账");
		//转账说明,UTF8编码,最多允许32个字符
		postMap.put("batch_remark", "测试转账");
		//转账金额单位为“分”。 总金额
		postMap.put("total_amount", 100);
		//。转账总笔数
		postMap.put("total_num", 1);


		List<Map> list = new ArrayList<>();
		Map<String, Object> subMap = new HashMap<>(4);
		//商家明细单号
		subMap.put("out_detail_no", outNo);
		//转账金额
		subMap.put("transfer_amount", 100);
		//转账备注
		subMap.put("transfer_remark", "明细备注1");
		//用户在直连商户应用下的用户标示
		subMap.put("openid", openId);
//		subMap.put("user_name", RsaCryptoUtil.encryptOAEP(userName, x509Certificate));
		list.add(subMap);
		postMap.put("transfer_detail_list", list);

        //发起转账操作
		String resStr = HttpUtil.postTransBatRequest(
				"https://api.mch.weixin.qq.com/v3/transfer/batches",
				GSON.toJson(postMap),
				wechatPayserialNo,
				mchid,
				privatekeypath);
}
import lombok.extern.slf4j.Slf4j;
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;


/**
 * 微信支付专用类 请求操作方法
 *
 * @author Administrator
 */
@Slf4j
public class HttpUtil {
	/**
	 * 发起批量转账API 批量转账到零钱
	 *
	 * @param requestUrl
	 * @param requestJson 组合参数
	 * @param wechatPayserialNo 商户证书序列号
	 * @param mchID4M  商户号 
	 * @param privatekeypath  商户私钥证书路径
	 * @return
	 */
	public static String postTransBatRequest(
			String requestUrl,
			String requestJson,
			String wechatPayserialNo,
			String mchID4M,
			String privatekeypath) {
		CloseableHttpClient httpclient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		HttpEntity entity = null;
		try {
			//商户私钥证书
			HttpPost httpPost = new HttpPost(requestUrl);
			// NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误
			httpPost.addHeader("Content-Type", "application/json");
			httpPost.addHeader("Accept", "application/json");
			//"55E551E614BAA5A3EA38AE03849A76D8C7DA735A");
			httpPost.addHeader("Wechatpay-Serial", wechatPayserialNo);
			//-------------------------核心认证 start-----------------------------------------------------------------
			String strToken = VechatPayV3Util.getToken("POST",
					"/v3/transfer/batches",
					requestJson,mchID4M,wechatPayserialNo, privatekeypath);
			
			log.error("微信转账token "+strToken);
			// 添加认证信息
			httpPost.addHeader("Authorization",
					"WECHATPAY2-SHA256-RSA2048" + " "
							+ strToken);
			//---------------------------核心认证 end---------------------------------------------------------------
			httpPost.setEntity(new StringEntity(requestJson, "UTF-8"));
			//发起转账请求
			response = httpclient.execute(httpPost);
			entity = response.getEntity();//获取返回的数据
			log.info("-----getHeaders.Request-ID:"+response.getHeaders("Request-ID"));
			return EntityUtils.toString(entity);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭流
		}
		return null;
	}

}

微信转账 token 签名认证


import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Random;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import org.springframework.util.StringUtils;

@Slf4j
public class VechatPayV3Util {

	/**
	 * 
	 * @param method 请求方法 post
	 * @param canonicalUrl 请求地址
	 * @param body 请求参数
	 * @param merchantId 这里用的商户号
	 * @param certSerialNo 商户证书序列号
	 * @param keyPath 商户证书地址
	 * @return
	 * @throws Exception
	 */
	public static String getToken(
			String method,
			String canonicalUrl,
			String body,
			String merchantId,
			String certSerialNo,
			String keyPath) throws Exception {
		String signStr = "";
		//获取32位随机字符串
        String nonceStr = getRandomString(32);
		//当前系统运行时间
		long timestamp = System.currentTimeMillis() / 1000;
		if (StringUtils.isEmpty(body)) {
			body = "";
		}
		//签名操作
		String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body);
		//签名操作
		String signature = sign(message.getBytes("utf-8"), keyPath);
		//组装参数
		signStr = "mchid=\"" + merchantId + "\",timestamp=\"" +  timestamp+ "\",nonce_str=\"" + nonceStr
				+ "\",serial_no=\"" + certSerialNo + "\",signature=\"" + signature + "\"";
		
		return signStr;
	}

	public static String buildMessage(String method, String canonicalUrl, long timestamp, String nonceStr, String body) {
//		String canonicalUrl = url.encodedPath();
//		if (url.encodedQuery() != null) {
//			canonicalUrl += "?" + url.encodedQuery();
//		}
		return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
	}

	public static String sign(byte[] message, String keyPath) throws Exception {
		Signature sign = Signature.getInstance("SHA256withRSA");
		sign.initSign(getPrivateKey(keyPath));
		sign.update(message);
		return Base64.encodeBase64String(sign.sign());
	}

	/**
	   * 微信支付-前端唤起支付参数-获取商户私钥
	   *
	   * @param filename 私钥文件路径  (required)
	   * @return 私钥对象
	   */
	  public static PrivateKey getPrivateKey(String filename) throws IOException {

	  	log.error("签名 证书地址是 "+filename);
	      String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
	      try {
	          String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
	                  .replace("-----END PRIVATE KEY-----", "")
	                  .replaceAll("\\s+", "");
	          //System.out.println("--------privateKey---------:"+privateKey);
	          KeyFactory kf = KeyFactory.getInstance("RSA");
	          return kf.generatePrivate(
	                  new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
	      } catch (NoSuchAlgorithmException e) {
	          throw new RuntimeException("当前Java环境不支持RSA", e);
	      } catch (InvalidKeySpecException e) {
	          throw new RuntimeException("无效的密钥格式");
	      }
	  }
	/**
	 * 获取随机位数的字符串
	 * @param length
	 * @return
	 */
	public static String getRandomString(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();
	}
}

调试中遇到的几个错误问题记录一下:

首先需要开通商家转账到零钱,然后进行产品设置

接口返回:{"code":"NO_AUTH","message":"本商户号未配置API发起能力"}

开启后才可以进行API调用
第二个问题:{"code":"NOT_ENOUGH","message":"账户余额不足,请充值"}

需要给运营账户进行充值,之前的版本是可以直接扣零钱的 

整体流程也就这样了,大家遇到问题也可以互相讨论一下

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
开发java微信转账零钱功能,需要进行以下几个步骤。 首先,我们需要在微信开放平台注册一个开发者账号,并创建一个应用。在创建应用时,需要选择相应的应用类型,如公众号、小程序或移动应用程序。 接下来,我们需要使用微信支付API实现转账功能微信支付提供了一套完整的API,包括支付、退款、查询余额等功能。我们可以通过调用相应的API实现微信转账零钱功能。 在进行转账之前,需要用户授权认证和绑定银行卡。用户需要在微信中进行授权认证,以便我们的应用可以获得用户的授权和支付权限。 一旦用户完成授权认证,我们就可以开始调用API实现转账功能了。首先,我们需要通过API微信支付申请支付的权限和生成支付订单。然后,用户可以选择转账的金额和目标账户,如零钱账户。 在用户确认转账后,我们使用API转账请求发送给微信支付平台,并等待平台的响应。如果转账成功,平台将返回一个响应消息,并更新用户的零钱账户余额。 最后,我们可以根据需要进行适当的错误处理和结果展示,以便用户能够及时了解转账结果和可能的错误原因。 总结起来,开发java微信转账零钱功能需要注册开发者账号、创建应用、使用微信支付API实现转账功能,并进行用户授权认证和绑定银行卡等步骤。此外,还需要处理错误和结果展示等细节,以提供良好的用户体验。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值