应用场景
微信支付并没有为小程序付款到零钱(企业付款到零钱)给出对应的Demo,所以写出demo供大家参考,大家可以根据自己的需求进行对应的更改以满足实际开发。
企业付款为企业提供付款至用户零钱的能力,支持通过API接口付款,或通过微信支付商户平台(pay.weixin.qq.com)网页操作付款。
应场景:小程序抽取红包,积分换取金额等
首先微信相关配置
public class WxPayConfig {
// 小程序appid
public static final String APP_ID = "APP_ID";
// APP——Secret
public static final String APP_SECRET = "APP_SECRET";
// 微信支付的商户号
public static final String MCH_ID = "MCH_ID";
// 微信支付的商户秘钥
public static final String KEY = "KEY ";
// 支付成功后的服务器回调url
public static final String notify_url = "notify_url";
// 签名方式,固定值
public static final String SIGNTYPE = "MD5";
// 交易类型,小程序支付的固定值为JSAPI
public static final String TRADETYPE = "JSAPI";
// 微信统一下单接口地址
public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 微信直接转账到个人请求
public static final String TRANSFOR_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
public static final String STATIC_NUM = "a0A0b1B2c1C3d2D1e3E2f4F3g5G7h4H6i5Ij4J9k5K6l6Lm7M7n8N8o9Op0PqQrRsStTuUv9VwWxXy8YzZ";
}
证书下载
企业支付的话需要到支付平台去下载证书
微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->证书下载
表4.2:证书文件说明
证书附件 | 描述 | 使用场景 | 备注 |
pkcs12格式 (apiclient_cert.p12、 | 包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份 | 撤销、退款申请API中调用 | windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户ID(如:10010000) |
证书pem格式 (apiclient_cert.pem) | 从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制 | PHP等不能直接使用p12文件,而需要使用pem,为了方便您使用,已为您直接提供 | 您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem |
证书密钥pem格式 (apiclient_key.pem) | 从apiclient_key.pem中导出密钥部分的文件,为pem格式 | PHP等不能直接使用p12文件,而需要使用pem,为了方便您使用,已为您直接提供 | 您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem |
使用商户证书
- ◆ apiclient_cert.p12是商户证书文件,除PHP外的开发均使用此证书文件。
- ◆ 商户如果使用.NET环境开发,请确认Framework版本大于2.0,必须在操作系统上双击安装证书apiclient_cert.p12后才能被正常调用。
- ◆ 商户证书调用或安装都需要使用到密码,该密码的值为微信商户号(mch_id)
商户证书安全
1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载;
2.建议将证书文件名改为复杂且不容易猜测的文件名;
3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。
参数说明
接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
请求需要双向证书。
请求参数说明
字段名 | 变量名 | 必填 | 示例值 | 类型 | 描述 |
商户账号appid | mch_appid | 是 | wx8888888888888888 | String | 申请商户号的appid或商户号绑定的appid |
商户号 | mchid | 是 | 1900000109 | String(32) | 微信支付分配的商户号 |
设备号 | device_info | 否 | 013467007045764 | String(32) | 微信支付分配的终端设备号 |
随机字符串 | nonce_str | 是 | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | String(32) | 随机字符串,不长于32位 |
签名 | sign | 是 | C380BEC2BFD727A4B6845133519F3AD6 | String(32) | 签名,详见签名算法 |
商户订单号 | partner_trade_no | 是 | 10000098201411111234567890 | String | 商户订单号,需保持唯一性 (只能是字母或者数字,不能包含有符号) |
用户openid | openid | 是 | oxTWIuGaIt6gTKsQRLau2M0yL16E | String | 商户appid下,某用户的openid |
校验用户姓名选项 | check_name | 是 | FORCE_CHECK | String | NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名 |
收款用户姓名 | re_user_name | 可选 | 王小王 | String | 收款用户真实姓名。 如果check_name设置为FORCE_CHECK,则必填用户真实姓名 |
金额 | amount | 是 | 10099 | int | 企业付款金额,单位为分 |
企业付款描述信息 | desc | 是 | 理赔 | String | 企业付款操作说明信息。必填。 |
Ip地址 | spbill_create_ip | 是 | 192.168.0.1 | String(32) | 该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP。 |
这里要注意金额至少为一块(100分)
发送红包请求
(核心代码)
public static Map<String, String> sendRedPacket(String openId, Long money, String orderNo, String ipAddress)
throws Exception {
// 生成的随机字符串
String nonce_str = getRandomStringByLength(32);
String totleMoney = money + "";
String desc = "现金红包测试";
// 组装参数,用户生成统一下单接口的签名
Map<String, String> packageParams = new HashMap<String, String>();
packageParams.put("mch_appid", WxPayConfig.APP_ID);
packageParams.put("mchid", WxPayConfig.MCH_ID);
packageParams.put("nonce_str", nonce_str);
packageParams.put("partner_trade_no", orderNo);// 商户订单号
packageParams.put("openid", openId);
packageParams.put("check_name", "NO_CHECK");
packageParams.put("amount", totleMoney);
packageParams.put("desc", desc);// 支付成功后的回调地址
packageParams.put("spbill_create_ip", ipAddress);
String prestr = PayUtil.createLinkString(packageParams);
// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
String mysign = PayUtil.sign(prestr, WxPayConfig.KEY, "utf-8").toUpperCase();
packageParams.put("sign", mysign);
String xml = "<xml><mch_appid>" + WxPayConfig.APP_ID + "</mch_appid><mchid>" + WxPayConfig.MCH_ID
+ "</mchid><nonce_str>" + nonce_str + "</nonce_str><partner_trade_no>" + orderNo
+ "</partner_trade_no><openid>" + openId + "</openid><check_name>NO_CHECK</check_name><amount>"
+ totleMoney + "</amount><desc>" + desc + "</desc><spbill_create_ip>" + ipAddress
+ "</spbill_create_ip><sign>" + mysign + "</sign></xml>";
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// p12证书路径
FileInputStream instream = new FileInputStream(new File("C:/Users/Liao/Desktop/dangj/cert/apiclient_cert.p12"));
// 微信支付的商户号
keyStore.load(instream, "商户号".toCharArray());
instream.close();
// 微信支付的商户号
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "商户号".toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpPost httpost = new HttpPost(WxPayConfig.TRANSFOR_URL);
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(xml, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
// 打印返回慘呼
System.out.println(JSON.toJSONString(jsonStr));
EntityUtils.consume(entity);
return null;
}
PayUtils签名算法
public class PayUtil {
/**
* 签名字符串
*
* @param text需要签名的字符串
* @param key
* 密钥
* @param input_charset编码格式
* @return 签名结果
*/
public static String sign(String text, String key, String input_charset) {
text = text + "&key=" + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* 签名字符串
*
* @param text需要签名的字符串
* @param sign
* 签名结果
* @param key密钥
* @param input_charset
* 编码格式
* @return 签名结果
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text + "&key=" + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
if (mysign.equals(sign)) {
return true;
} else {
return false;
}
}
}
随机数生成方法
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();
}
调用发送红包
public static void main(String[] args) {
try {
sendRedPacket("openId", 100L, "orderNo", "192.168.0.1");
} catch (Exception e) {
e.printStackTrace();
}
}
错误信息处理
如果出现CA证书错误,查看证书路径和支付商户号是否正确
错误NOAUTH,提示产品权限验证失败。需要到微信支付平台->产品中心->企业付款到零钱,开通企业付款到零钱,开通需求:微信支付商户后台需要90天、连续正常交易流水30天,才可以申请开通“企业付款到零钱”产品。