1.企業號 https://work.weixin.qq.com/html
2.已開通支付功能的微信商戶號 https://pay.weixin.qq.com/java
微信商戶平臺(pay.weixin.qq.com)-->帳戶中心-->帳戶設置-->API安全web
(放置到web訪問沒法下載的地方,防止證書下載泄露)算法
企業管理後臺-->應用管理-->企業支付-->綁定商戶平臺商戶號c#
官方文檔地址:https://work.weixin.qq.com/api/doc/90000/90135/90274api
發送紅包接口
安全
請求方式:POST(HTTPS)
請求地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendworkwxredpack
是否須要證書:是
數據格式:xml
部分容易混淆參數說明:
商戶號 微信商戶平臺-->帳戶中心-->商戶信息-->商戶號
公衆帳號appid 企業微信後臺-->個人企業-->企業信息-->企業ID
用戶openid 使用企業用戶的userId轉爲openId 官方文檔:http://work.weixin.qq.com/api/doc#11279
微信支付簽名算法
第一步,設全部發送或者接收到的數據爲集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特別注意如下重要規則:
參數名ASCII碼從小到大排序(字典序);若是參數的值爲空不參與簽名;參數名區分大小寫;驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值做校驗。微信接口可能增長字段,驗證簽名時必須支持增長的擴展字段
第二步,在stringA最後拼接上key獲得stringSignTemp字符串,並對stringSignTemp進行MD5運算,再將獲得的字符串全部字符轉換爲大寫,獲得sign值signValue。
附:key爲商戶平臺API密鑰裏面設置的key,key設置以後不能查看,建議設置後另外保存一份,以避免遺忘。另外,再建議將key順便保存到 商戶平臺->產品中心->企業微信收款->API密鑰管理 裏面,這樣後續企業微信收款才能正常使用。
簽名字段:
除sign字段外全部字段都參與簽名(包括企業微信簽名字段workwx_sign一塊兒參與簽名).
企業微信簽名算法
第一步: 設全部發送或者接收到的數據爲集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
注意:
參數名ASCII碼從小到大排序(字典序)若是參數的值爲空不參與簽名參數名區分大小寫傳送的sign參數不參與簽名,將生成的簽名與該sign值做校驗
第二步: 在stringA最後拼接上企業微信支付應用secret(參見企業微信管理端支付應用頁面的secret),獲得stringSignTemp字符串,並對stringSignTemp進行MD5運算,再將獲得的字符串全部字符轉換爲大寫,獲得sign值signValue。
企業微信簽名字段說明:
發紅包api固定以下幾個字段參與簽名:
act_name
mch_billno
mch_id
nonce_str
re_openid
total_amount
wxappid
第一步: 對參數按照key=value的格式,並按照參數名ASCII字典序排序以下
stringA=」act_name=XXX&mch_billno=11111234567890&mch_id=10000098&nonce_str=qFKEgfig76DF9912fewmkp&re_openid=oxTWIuGaIt6gTKsQRLau2M0yL16E&total_amount=100&wxappid=wx12345678
第二步:拼接企業微信支付應用secret(參見企業微信管理端支付應用頁面)
stringSignTemp=」stringA&secret=192006250b4c09247ec02edce69f6a2d」
sign=MD5(stringSignTemp).toUpperCase()
/** * 發送紅包請求
* @param openId 用戶openid
* @param mchBillNo 訂單號
* @param totalAmount 發送金額(單位:分)
* @param wishing 祝福語
* @param actName 項目名稱(在微信端不顯示)
* @param senderName 發送人姓名
* @return
*/
public
Boolean sendWorkWxRedPack(String openId,String mchBillNo,
int
totalAmount,String wishing,String actName,String senderName)
throws
Exception
Map<String,String> paramMap =
new
TreeMap<>();
paramMap.put(
"act_name"
,actName);
//項目名稱
paramMap.put(
"mch_billno"
,mchBillNo);
paramMap.put(
"mch_id"
,mchId);
paramMap.put(
"nonce_str"
,WXPayUtil.generateNonceStr());
paramMap.put(
"re_openid"
,openId);
//openId
paramMap.put(
"total_amount"
,String.valueOf(totalAmount));
//金額,單位分
paramMap.put(
"wxappid"
,corpId);
String workWxSign = gerCompanySign(paramMap);
//生成企業簽名
paramMap.put(
"workwx_sign"
,workWxSign);
paramMap.put(
"wishing"
,wishing);
//紅包祝福語
paramMap.put(
"remark"
,
"星宏"
);
paramMap.put(
"sender_name"
,senderName);
paramMap.put(
"sender_header_media_id"
,
"1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0"
);
paramMap.put(
"scene_id"
,
"PRODUCT_4"
);
String paySign = gerPaySign(paramMap);
//生成微信支付簽名
paramMap.put(
"sign"
,paySign);
String xmlBody = WXPayUtil.mapToXml(paramMap);
String request = requestOnce(CP_SEND_WORK_WX_REDPACK,xmlBody,
8
*
1000
,
8
*
1000
,
true
);
Map<String,String> retMap = WXPayUtil.xmlToMap(request);
if
(retMap.containsKey(
"result_code"
)&&
"SUCCESS"
.equals(retMap.get(
"result_code"
)))
log.debug(
"微信發送參數"
+xmlBody);
log.debug(
"##############發送紅包成功:"
+request);
return
true
;
}
else
{
log.error(
"微信發送參數"
+xmlBody);
log.error(
"###############發送紅包失敗,返回緣由:"
+request);
return
false
;
}
}
/**
* 生成簽名. 注意,若含有sign_type字段,必須和signType參數保持一致。
* @param data 待簽名數據
* @param key API密鑰
* @return 簽名
*/
private
static
String generateSignature(
final
Map<String, String> data, String key,String type)
throws
Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(
new
String[keySet.size()]);
Arrays.sort(keyArray);
StringBuffer sb =
new
StringBuffer();
for
(String k : keyArray) {
if
(k.equals(WXPayConstants.FIELD_SIGN)) {
continue
;
}
// 參數值爲空,則不參與簽名
if
(data.get(k).trim().length() >
0
)
sb.append(k).append(
"="
).append(data.get(k).trim()).append(
"&"
);
}
if
(
"companyPaySecret"
.equals(type)){
sb.append(
"secret="
).append(key);
log.debug(
"企業拼接結果"
+sb.toString());
}
if
(
"paySecret"
.equals(type)){
sb.append(
"key="
).append(key);
log.debug(
"微信支付拼接結果"
+sb.toString());
}
String sign = SecureUtil.md5(sb.toString()).toUpperCase();
log.debug(
"生成:"
+sign);
return
sign;
}
/**
* 發送https請求
* @param url 請求地址
* @param data 請求體
* @param connectTimeoutMs 鏈接超時時間
* @param readTimeoutMs 讀取超時時間
* @param useCert 是否使用證書,針對退款、撤銷等操做
*/
private
String requestOnce(String url, String data,
int
connectTimeoutMs,
int
readTimeoutMs,
boolean
useCert)
throws
Exception {
try
( InputStream certStream =
this
.getClass().getClassLoader().getResourceAsStream(
"apiclient_cert.p12"
)){
BasicHttpClientConnectionManager connManager;
if
(useCert) {
// 證書
char
[] password = mchId.toCharArray();
KeyStore ks = KeyStore.getInstance(
"PKCS12"
);
ks.load(certStream, password);
// 實例化密鑰庫 & 初始化密鑰工廠
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password);
// 建立 SSLContext
SSLContext sslContext = SSLContext.getInstance(
"TLS"
);
sslContext.init(kmf.getKeyManagers(),
null
,
new
SecureRandom());
SSLConnectionSocketFactory sslConnectionSocketFactory =
new
SSLConnectionSocketFactory(sslContext,
new
String[]{
"TLSv1"
},
null
,
new
DefaultHostnameVerifier());
connManager =
new
BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register(
"http"
, PlainConnectionSocketFactory.getSocketFactory())
.register(
"https"
, sslConnectionSocketFactory)
.build(),
null
,
null
,
null
);
}
else
{
connManager =
new
BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register(
"http"
, PlainConnectionSocketFactory.getSocketFactory())
.register(
"https"
, SSLConnectionSocketFactory.getSocketFactory())
.build(),
null
,
null
,
null
);
}
HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connManager).build();
HttpPost httpPost =
new
HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity =
new
StringEntity(data,
"UTF-8"
)
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
return
EntityUtils.toString(httpEntity,
"UTF-8"
);
}